vue 組件通訊總結 (非vuex和Event Bus)

方式一覽

  1. props && emit
  2. v-model
  3. $children && $parent
  4. $listeners && $attrs
  5. .sync

prop && emit 方式

父組件經過 props 傳遞數據給子組件,子組件經過 emit 發送事件傳遞數據給父組件。這是最經常使用的父子組件通訊方式,符合單向數據流,即子組件不能直接修改 props, 而是必須經過發送事件的方式告知父組件修改數據。因爲是經常使用的方式,在這也很少囉嗦了。html

v-model 方式

v-model實現的通訊其本質上仍是上面的propsemit方式,使用v-model更像是一種語法糖。文檔介紹vue

先舉個栗子:vuex

// 這是父組件
<template>
  <div>
    <child v-model="msg"></child>
    <p>{{msg}}</p>
  </div>
</template>

<script>
import child from "../components/Child";
export default {
  data() {
    return {
      msg: "hello"
    };
  },
  components: { child }
};
</script>

複製代碼
// 這是子組件
<template>
  <div>
      <input :value="value" @input="$emit('input',$event.target.value)">
  </div>
</template>

<script>
export default {
  props: ["value"]
};
</script>
複製代碼

父組件使用子組件時,使用v-model綁定父組件msg數據,這會在子組件裏解析成名爲 value 的 prop 和名爲 input 的事件,因此子組件裏的props選項裏必須寫成value,在$emit事件裏也需寫成input事件。此時當你在子組件輸入時,就會改變父組件的msg值。api

使用 model 選項自定義 props 和 event

上面說了,props選項裏必須寫value,事件也必須是input。這是默認狀況下的解析,其實咱們也能夠自定義 props 和 event,使用model選項,文檔介紹。文檔中以複選框爲例,修改 props 和 event:數組

model: {
    prop: 'checked',
    event: 'change'
}
複製代碼

$children && $parent 方式

這兩個是vue提供的api,見名知意,在父組件裏使用 $children 訪問子組件,在子組件裏使用$parent訪問父組件。bash

舉個簡單栗子:ide

// 這是子組件

<template>
  <div>
     {{$parent.msg}}   // 子組件顯示父組件數據
  </div>
</template>

<script>
export default {
  data() {
    return {
      child_msg: "我是子組件數據"
    };
  },
  mounted() {
    this.$parent.test(); // 子組件執行父組件方法
  }
};
</script>
複製代碼
// 這是父組件

<template>
  <div>
    <child/>
  </div>
</template>

<script>
import child from "../components/Child";
export default {
  data() {
    return {
      msg: "我是父組件的數據"
    };
  },
  components: { child },
  methods: {
    test() {
      console.log("我是父組件的方法,被執行");
    }
  },
  mounted() {
    console.log(this.$children[0].child_msg); // 執行子組件方法
  }
};
</script>
複製代碼

【注意】 $children 是數組,因此當只有一個子組件時,使用[0]獲取。當有多個子組件時,它並不保證順序,也不是響應式的。ui

$listeners 方式

初看此api的定義,我也是似懂非懂:this

包含了父做用域中的 (不含 .native 修飾器的) v-on 事件監聽器。它能夠經過 v-on="$listeners" 傳入內部組件——在建立更高層次的組件時很是有用spa

文檔這裏也描述了它的使用方法: 文檔介紹

在查看一些博客時,要麼拿官方例子,要麼一大堆介紹,其實我看的也是一臉懵逼。後來本身慢慢試着用了下,也大概明白它是幹嗎的。個人理解:在多層嵌套組件的業務中,使用$listeners可使用更少的代碼來完成事件通訊。

仍是以代碼來講明,以下圖,咱們來實現組件B 到 父組件 的通訊,

通常嵌套層級太多時,咱們可能就會考慮vuex,但只傳遞數據,而不作中間處理,有點大材小用,因此如上圖這樣的,咱們可能仍是使用emit方式來通訊,無非多傳一層,多寫點代碼。那麼如今,有了$listeners,咱們能夠更方便的來實現,我儘可能用最少的代碼來實現下:

就從最下面的B組件開始,它有一個按鈕,點擊時觸發實例上的事件getFromB

// 組件B

<template>
  <div>
    <button @click="handleClick">B組件按鈕</button>
  </div>
</template>

<script>
export default {
  methods: {
    handleClick() {
      this.$emit("getFromB");
    }
  }
};
</script>
複製代碼

A組件 包裹 B組件,至關因而父組件與B組件的中轉站,在不用$listeners時,咱們可能會在這裏再觸發一個事件,如今不須要這樣了,咱們這樣:

// 組件A

<template>
  <div>
    <child-b v-on="$listeners" />
  </div>
</template>

<script>
import childB from "../components/ChildB";
export default {
  components: {
    childB
  },
  mounted() {
    console.log(this.$listeners);
  }
};
</script>
複製代碼

只須要加一句v-on="$listeners"便可。好奇的咱們也能夠 mounted 時打印一下$listeners

父組件,顯而易見,咱們直接綁定getFromB事件便可:

// 父組件

<template>
  <div>
   <child-a v-on:getFromB="fromB"/>
  </div>
</template>

<script>
import childA from "../components/ChildA";
export default {
  components: { childA },
  methods: {
    fromB() {
      console.log("B組件觸發");
    }
  }
};
</script>
複製代碼

這就是$listeners的簡單用法,說到這裏,你應該意識到,當組件嵌套不少層時,不借助 vuex,咱們也能夠較方便地實現通訊了。

說到這裏,我還要提一個api,就是$attrs。它與$listeners的關係就比如 props 與 emit 的關係,用來向底層組件傳遞屬性。先貼上它的定義:

包含了父做用域中不做爲 prop 被識別 (且獲取) 的特性綁定 (class 和 style 除外)。當一個組件沒有聲明任何 prop 時,這裏會包含全部父做用域的綁定 (class 和 style 除外),而且能夠經過 v-bind="$attrs" 傳入內部組件——在建立高級別的組件時很是有用。

咱們回想下,若是使用 props 向孫組件傳遞數據時,在中間組件裏,咱們是要一層層使用 props 選項來接收,而後再傳遞的。那麼$attrs的做用就是在沒到目標子組件時,不使用props接收數據,直到到達須要數據的組件時,再使用props接收。

在我看別的博客時,都是這兩個api一塊兒說的,代碼比較多,爲了清晰,我把上面代碼多餘的代碼刪掉,只演示$attrs的使用:

父組件傳遞一個屬性toB,意爲是給B組件用的:

// 父組件

<template>
  <div>
   <child-a toB="hello"/>
  </div>
</template>

<script>
import childA from "../components/ChildA";
export default {
  components: { childA }
};
</script>
複製代碼

A組件使用v-bind="$attrs"便可,不須要 props 接收,實際上也不能夠接收,看定義

// 組件 A

<template>
  <div>
     <child-b v-bind="$attrs" />
  </div>
</template>

<script>
import childB from "../components/ChildB";
export default {
  components: { childB }
};
</script>
複製代碼

B組件是咱們的最後子組件,它用到toB屬性,因此使用 props 選項接收了

<template>
  <div>
    <p>父組件傳來數據:{{toB}}</p>
  </div>
</template>

<script>
export default {
  props: ["toB"]
};
</script>
複製代碼

從這個簡單的例子,咱們能夠知道,當組件嵌套層級不少時,屬性傳遞變得不要太方便。最後還要提一個inheritAttrs選項,它通常配合$attrs使用,這裏我就再也不多說了。文檔介紹

.sync 方式

此方法其實用的也很多,它在 Vue 1.x 裏的做用是對一個 prop 進行「雙向綁定「。但在 Vue 2 以後是隻容許單向數據流的,因此如今即便它看起來像是真正的「雙向綁定」,本質上也只是做爲一個編譯時的語法糖存在而已。

舉個計數器的例子:

// 父組件

<template>
  <div>
    {{num}}
   <child-a :count.sync="num" />
  </div>
</template>

<script>
import childA from "../components/ChildA";
export default {
  data() {
    return {
      num: 0
    };
  },
  components: { childA }
};
</script>
複製代碼
// 子組件

<template>
  <div>
     <div @click="handleAdd">ADD</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      counter: this.count
    };
  },
  props: ["count"],
  methods: {
    handleAdd() {
      this.$emit("update:count", ++this.counter);
    }
  }
};
</script>
複製代碼

嗯,看起來彷佛更有逼格。

結語

這麼看下來,除了$children$parent 是直接獲取的,其餘都跟 props 和 emit 息息相關。具體怎麼用,本身看着辦唄。

最後,新年快樂!

相關文章
相關標籤/搜索