vue 組件通信的多種方式
前言
在vue中, 組件的關系不外乎以下三種:
組件是需要通信的,在開發中,常用到的通信方式有:vuex、eventBus、以及props與emit、$parent與$children,除此之外,還有provide與inject、$attrs與$listeners等。
一、vuex
這個相信大傢用的很多瞭,簡單回顧一下:
- State:放狀態的地方
- Mutation:唯一修改狀態的地方,不支持異步
- Action:通過調用Mutation中的方法來達到修改狀態的目的,支持異步
- Getter:可以理解為計算屬性
- Module:模塊,每個模塊擁有自己的 state、mutation、action、getter
簡單的使用這裡不贅述,提一下module裡面的命名空間。
如果希望你的模塊具有更高的封裝度和復用性,你可以通過添加 namespaced: true 的方式使其成為帶命名空間的模塊。當模塊被註冊後,它的所有 getter、action 及 mutation 都會自動根據模塊註冊的路徑調整命名
這樣,在使用的時候我們就可以這樣用瞭:
二、eventBus
這個稱為‘事件總線’,簡單看下是怎麼使用的:
- 初始化
首先是初始化一個eventBus,可以綁定到vue原型上,也可以綁定到window對象上,還可以抽出來當做一個模塊,在需要的時候再引入。這裡直接綁定到vue原型上:
- 創建事件和刪除事件
在需要的組件上創建和刪除事件:
- 觸發事件
最後就是在需要的地方觸發事件瞭
三、props/emit
這個不用多說瞭,父子通信用的最多的應該就是這個瞭。當然,如果以子組件為跳板,也可以做到祖孫之間通信,不過比較麻煩。不建議這樣操作。
四、$parent/$children
$parent直接訪問的就是父實例,而$children則返回的是實例數組。所以我一般都是$parent搭配$refs使用。
五、$attrs/$listeners
這兩個可能會用的比較少,來看下官網的介紹:
怎麼理解呢,簡單來講就是,$attrs接收除瞭prop、style、class之外的所有綁定屬性,$listeners則接收除瞭被.native修飾的所有綁定事件。具體來看下例子:
<template> <div> <p>父組件</p> <input type="text" v-model="formData.inputValue" /> <p>子組件</p> <Son :inputValue="formData.inputValue" :otherValue="otherValue" @success="success" @input.native="handleInput" v-bind="$attrs" v-on="$listeners" ></Son> </div> </template> <script> import Son from "./son.vue"; export default { components: { Son }, provide() { return { father: this.formData, }; }, data() { return { formData: { inputValue: "123", }, otherValue: 999, }; }, methods: { success(data) { console.log(data); }, handleInput() {}, }, }; </script>
<template> <div> <input type="text" v-model="inputValue" @change="handleChange" /> </div> </template> <script> export default { props: { inputValue: String, }, created() { console.log(this.$attrs, "son---$attrs"); console.log(this.$listeners, "son---$listeners"); }, methods: { handleChange() { this.father.inputValue = this.inputValue; }, }, }; </script>
按照之前的理解,$attrs應該隻能接收到otherValue,$listeners則隻能接收到success事件,看下打印結果:
結果確實也是這樣的。除此之外,還可傳遞給孫組件:
<template> <div> <input type="text" v-model="inputValue" @change="handleChange" /> <GrandSon v-bind="$attrs" v-on="$listeners"></GrandSon> </div> </template> <script> import GrandSon from "./grandSon.vue"; export default { components: { GrandSon }, props: { inputValue: String, }, created() { console.log(this.$attrs, "son---$attrs"); console.log(this.$listeners, "son---$listeners"); }, methods: { handleChange() { this.father.inputValue = this.inputValue; }, }, }; </script>
<template> <div> <input type="text" v-model="inputValue" @change="handleChange" /> </div> </template> <script> export default { props: { inputValue: String, }, created() { console.log(this.$attrs, "grandSon---$attrs"); console.log(this.$listeners, "grandSon---$listeners"); }, methods: { handleChange() { this.father.inputValue = this.inputValue; }, }, }; </script>
通過這種方式,祖孫之間也實現瞭通信。
六、provide/inject
provide/inject可以在一個祖先組件中向它的所有後輩組件註入一個依賴,隻要上下遊關系成立就能生效。簡單的理解就是provide是註入數據,inject是獲取數據。所以provide是用於父組件,inject是用於子孫組件。provide應該是一個對象或者返回一個對象的函數,inject應該是一個字符串數組或者一個對象。官網提到這麼一句話:
提示:provide 和 inject 綁定並不是可響應的。這是刻意為之的。然而,如果你傳入瞭一個可監聽的對象,那麼其對象的 property 還是可響應的。
這句話怎麼理解呢?字面理解就是你要想在上下遊傳遞的那個數據是可響應的,那麼就應該以對象的形式傳遞,先試一下以基本數據類型的形式傳遞,看下例子:
父組件:
<template> <div> <p>父組件</p> <input type="text" v-model="inputValue" /> <p>子組件</p> <Son></Son> <p>孫組件</p> <GrandSon></GrandSon> </div> </template> <script> import Son from "./son.vue"; import GrandSon from "./grandSon.vue"; export default { components: { Son, GrandSon }, provide() { return { father: this.inputValue, }; }, data() { return { inputValue: "123", }; }, }; </script>
子組件:
<template> <div> <input type="text" v-model="inputValue" @change="handleChange" /> </div> </template> <script> export default { inject: ["father"], data() { return { inputValue: "", }; }, watch: { father(val) { console.log(val, "val"); this.inputValue = val; }, }, created() { console.log(this, "this"); }, methods: { handleChange() { this.father.inputValue = this.inputValue; }, }, }; </script>
在子組件打印this:
可以看到,父組件的inputValue值是被註入到子組件當中的。但卻監聽不到這個father。
然後,我們改成以對象的形式進行註入:
<template> <div> <p>父組件</p> <input type="text" v-model="formData.inputValue" /> <p>子組件</p> <Son></Son> <p>孫組件</p> <GrandSon></GrandSon> </div> </template> <script> import Son from "./son.vue"; import GrandSon from "./grandSon.vue"; export default { components: { Son, GrandSon }, provide() { return { father: this.formData, }; }, data() { return { formData: { inputValue: "123", }, }; }, }; </script>
<template> <div> <input type="text" v-model="inputValue" @change="handleChange" /> </div> </template> <script> export default { inject: ["father"], data() { return { inputValue: "", }; }, watch: { 'father.inputValue'(val){ console.log(val, "val"); this.inputValue = val; }, }, created() { console.log(this, "this"); }, methods: { handleChange() { this.father.inputValue = this.inputValue; }, }, }; </script>
這個時候我們看下打印的this以及效果:
這樣就可以實現數據的響應瞭。這裡有一個點需要註意,如果在父組件中將整個父組件的this註入到後代組件中,在後代組件中是不能通過深度監聽來監聽這個註入的對象的,會報堆棧溢出的錯誤。所以這裡我用的是this.formData
的形式註入。這樣在子孫組件中可以通過'father.inputValue'
這樣的形式監聽,也可以通過這樣的形式:
father: { handler(val) { console.log(val); }, deep: true, },
至於為什麼會導致這個問題,我們先看下深度監聽的實現方式:
這段註釋什麼意思呢,簡單理解就是vue是通過遞歸遍歷對象裡面的每一個屬性,將是對象的屬性收集起來進行監聽。眾所周知,遞歸是很容易引起堆棧溢出的,而看下this對象就不難理解為什麼會導致堆棧溢出瞭(太多瞭,而且是層層嵌套下去的)。
以上就是Vue組件通信的幾種方式,如果還要在扯一扯,瀏覽器的緩存也可以作為一種手段。。。
到此這篇關於vue 組件通信的幾種方式的文章就介紹到這瞭,更多相關vue 組件通信內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!