Vue元件通訊方式全面詳解
阿新 • • 發佈:2020-04-02
## vue元件通訊方式全面詳解
眾所周知,Vue主要思想就是元件化開發。因為,在實際的專案開發中,肯定會以元件的開發模式進行。形如頁面和頁面之間需要通訊一樣,Vue 元件和元件之間肯定也需要互通有無、共享狀態。接下來,我們就悉數給大家展示所有 Vue 元件之間的通訊方式。
### 元件關係
![](https://img2020.cnblogs.com/blog/1364810/202004/1364810-20200402100152682-587394401.png)
- App元件和A元件、A元件和B元件、B元件和C元件形成父子關係
- B元件和D元件形成兄弟關係
- App元件和C元件、App和B元件形成了隔代關係(其中的層級可能是多級,既隔多代)
### 元件通訊
------
這麼多的元件關係,那麼元件和元件之間又有哪些通訊的方式呢?各種方式的區別又是什麼?適用場景又是什麼呢?
#### props和$emit
這種方式是我們日常開發中應用最多的一種方式。
props以單向資料流的形式可以很好的完成父子元件的通訊
所謂單向資料流:就是資料只能通過 props 由父元件流向子元件,而子元件並不能通過修改 props 傳過來的資料修改父元件的相應狀態。至於為什麼這樣做,Vue 官網做出瞭解釋:
**\*所有的 prop 都使得其父子 prop 之間形成了一個單向下行繫結:父級 prop 的更新會向下流動到子元件中,但是反過來則不行。這樣會防止從子元件意外改變父級元件的狀態,從而導致你的應用的資料流向難以理解。*
**額外的,每次父級元件發生更新時,子元件中所有的 prop 都將會重新整理為最新的值。這意味著你不應該在一個子元件內部改變 prop。如果你這樣做了,Vue 會在瀏覽器的控制檯中發出警告。**
正因為這個特性,於是就有了對應的 `$emit`。`$emit` 用來觸發當前例項上的事件。對此,我們可以在父元件自定義一個處理接受變化狀態的邏輯,然後在子元件中如若相關的狀態改變時,就觸發父元件的邏輯處理事件。
```js
let Child = {
template: `
`,
props: ['msg'],
methods: {
handleClick() {
this.$emit('getChildData', '子元件資料')
}
},
}
let Parent = {
data() {
return {
msg: '小馬哥',
val:''
}
},
methods: {
getChildData(val) {
this.val = val;
}
},
template: `
`,
data() {
return {
msg: 'hello 小馬哥'
}
},
methods: {
handleClick() {
this.$bus.$emit('globalEvent',this.msg);
}
},
})
Vue.component('B', {
template: `
`,
})
```
#### $attrs和$listeners
通過 `props`進行元件通訊的方式只適合直接的父子元件,如果父元件A下面有子元件B,元件B下面有元件C,這時如果元件A直接想傳遞資料給元件C那就行不通了! 只能是元件A通過 props 將資料傳給元件B,然後元件B獲取到元件A 傳遞過來的資料後再通過 props 將資料傳給元件C。當然這種方式是非常複雜的,無關元件中的邏輯業務一種增多了,程式碼維護也沒變得困難,再加上如果巢狀的層級越多邏輯也複雜,無關程式碼越多!
針對這樣一個問題,Vue 2.4提供了`$attrs` 和`$listeners`來實現能夠直接讓元件A傳遞訊息給元件C
```js
Vue.component('A', {
template: `
`, mounted () { //讀取子元件資料,注意$children並不保證順序,也不是響應式的 console.log(this.$children[0].a) }, data() { return { msg: 'hello 小馬哥' } }, }) Vue.component('Child', { template: `
我是一個父元件
我是{{val}}
`, components: { Child } } let vm = new Vue({ el: '#app', template: ` `, components: { Parent } }) ``` 1. **父傳子**:父元件傳遞msg資料給子元件,通過v-bind繫結msg,子元件中直接可以用props接收繫結的資料 2. **子傳父**:子元件觸發相應的事件,通過$emit觸發事件傳遞資料,父元件中繫結對應的事件,通過$on監聽對應的事件 接收子元件傳遞的資料 #### EventBus-中央事件匯流排 如果想實現兄弟元件之間進行通訊,在專案規模不大的情況下,完全可以使用中央事件匯流排`EventBus`的方式。如果你的專案規模是大中型的,那我們會使用`vuex狀態管理` `EventBus`通過新建一個`Vue`事件`bus`物件,通過`bus.$emit`觸發事件,bus.$on監聽觸發的事件。 ```js Vue.component('A', { template: `我是A元件
我是B元件
{{aValue}}
`, data() { return { aValue: '' } }, created () { this.$bus.$on('globalEvent',(val)=>{ this.aValue = val; }) }, }) // 定義中央事件匯流排 let bus = new Vue(); // 將中央事件匯流排賦值給Vue.prototype中,這樣所有元件都能訪問到了 Vue.prototype.$bus = bus; let vm = new Vue({ el: '#app', template: `我是A元件
`, methods: { getCData(val) { alert(val) } }, data() { return { msg: 'hello 小馬哥' } }, }) Vue.component('B', { template: `我是B元件
`, // props: ['msg'], data() { return { } } }) Vue.component('C', { template: `我是C元件
{{$attrs.msg}}
`, methods: { handleClick() { this.$emit('getCData', 'C元件的資料') } }, data() { return { } } }) let vm = new Vue({ el: '#app', template: ` `, }) ``` - `$attrs`:包含了父作用域中不被 prop 所識別 (且獲取) 的特性繫結 (`class` 和 `style` 除外)。當一個元件沒有宣告任何 prop 時,這裡會包含所有父作用域的繫結屬性 (class和 `style` 除外),並且可以通過 `v-bind="$attrs"` 傳入內部元件。 - `$listeners`:包含了父作用域中的 (不含 `.native` 修飾器的) `v-on` 事件監聽器。它可以通過 `v-on="$listeners"` 傳入內部元件。 #### provide和inject 在父元件中通過 `provider` 來提供屬性,然後在子元件中通過 inject 來注入變數。不論子元件有多深,只要呼叫了 `inject` 那麼就可以注入在 provider 中提供的資料,而不是侷限於只能從當前父元件的 prop 屬性來獲取資料,只要在父元件的生命週期內,子元件都可以呼叫。這和 React 中的 `Context API` 有沒有很相似! ```js Vue.component('A', { template: `我是A元件
`, provide:{ a:"祖先A的資料" }, data() { return { msg: 'hello 小馬哥' } }, }) Vue.component('B', { template: `我是B元件
`, data() { return { } } }) Vue.component('C', { template: `我是C元件
{{a}}
`, inject:['a'], data() { return { } } }) let vm = new Vue({ el: '#app', template: ` `, }) ``` - 在 `parent` 元件中,通過 `provide` 屬性,以物件的形式向子孫元件暴露了一些屬性 - 在 `child` 元件中,通過 `inject` 屬性注入了 `parent` 元件提供的資料,實際這些通過 `inject` 注入的屬性是掛載到 Vue 例項上的,所以在元件內部可以通過 this 來訪問 > ⚠️ 注意:官網文件提及 provide 和 inject 主要為高階外掛/元件庫提供用例,並不推薦直接用於應用程式程式碼中。 #### $parent和$children 這裡要說的這種方式就比較直觀了,直接操作父子元件的例項。`$parent` 就是父元件的例項物件,而 `$children` 就是當前例項的直接子元件例項了,不過這個屬性值是陣列型別的,且並不保證順序,也不是響應式的。 ```js Vue.component('Parent', { template: `我是父元件
{{msg}}`, mounted () { //讀取子元件資料,注意$children並不保證順序,也不是響應式的 console.log(this.$children[0].a) }, data() { return { msg: 'hello 小馬哥' } }, }) Vue.component('Child', { template: `
我是孩子元件