Vue組件通信

總體來說,Vue中組件之間的通信場景如下圖:

組件通信場景

可以將其分為父子組件通信、兄弟組件通信、跨級(jí)組件通信。

1. 自定義事件

子組件-->父組件: 采用自定義事件,子組件用$emit()來觸發(fā)事件,父組件用$on()來監(jiān)聽子組件事件,父組件也可以直接在子組件的自定義標(biāo)簽上使用v-on來監(jiān)聽子組件觸發(fā)的自定義事件:

<!-- 自定義事件 -->
<body>
    <div id="app">
        <p>SUM: {{total}} </p>
        <!-- 父組件用v-on監(jiān)聽子組件事件,做出處理 -->
        <my-component 
            @increase = "handleGetTotal"
            @reduce = "handleGetTotal"
        ></my-component>
    </div>

    <script src = "https://unpkg.com/vue/dist/vue.min.js"></script>
    <script>
        Vue.component('my-component',{
            template: '<div><button @click="handleIncrease">+1</button> <button @click="handleReduce">-1</button></div>',
            data: function() {
                return {
                    counter: 0
                }
            },
            methods: {
                handleIncrease: function() {
                    this.counter++;
                    //使用$emit觸發(fā)increase事件,第一個(gè)參數(shù)為自定義事件的名稱,第二個(gè)參數(shù)是要傳遞的數(shù)據(jù)
                    this.$emit('increase', this.counter);
                },
                handleReduce: function() {
                    this.counter--;
                    this.$emit('reduce', this.counter);
                }
            }
        });

        var app = new Vue({
            el: '#app',
            data: {
               total: 0,
            },
            methods: {
                handleGetTotal: function(total) {
                    this.total = total;
                }
            }
        })
    </script>
</body>  
執(zhí)行結(jié)果

子組件有兩個(gè)按鈕,分別實(shí)現(xiàn)+1和-1的效果,在改變counter后,通過$emit()把它傳遞給父組件,父組件通過v-on獲取并作出處理。

2. 使用v-model

Vue2.x 中也可以在自定義組件中使用v-model指令:

<!-- 自定義事件使用v-model -->
<body>
    <div id="app">
        <p>SUM: {{total}} </p>
        <my-component v-model="total"></my-component>
    </div>

    <script src = "https://unpkg.com/vue/dist/vue.min.js"></script>
    <script>
        Vue.component('my-component',{
            template: '<div><button @click="handleIncrease">+1</button><button @click="handleReduce">-1</button></div>',
            data: function() {
                return {
                    counter: 0
                }
            },
            methods: {
                handleIncrease: function() {
                    this.counter++;
                    this.$emit('input', this.counter);
                },
                handleReduce: function() {
                    this.counter--;
                    this.$emit('input', this.counter);
                }
            }
        });

        var app = new Vue({
            el: '#app',
            data: {
               total: 0,
            },
        })
    </script>
</body>  

所實(shí)現(xiàn)效果一樣,但是現(xiàn)在子組件$emit()的事件名為特殊的input,在父組件中,直接使用了v-model綁定一個(gè)數(shù)據(jù)total。這其實(shí)是一個(gè)語法糖,其實(shí)際實(shí)現(xiàn)為:

<my-component @input="handleGetTatal"></my-component>
...
methods: {
    handleGetTotal: function(total) {
        this.total = total;
    }
}

v-model還可以用來創(chuàng)建自定義的表單輸入組件,進(jìn)行數(shù)據(jù)雙向綁定。

3. 非父子組件通信--中央事件總線

在Vue 2.x中,推薦使用一個(gè)空的Vue實(shí)例作為中央事件總線(bus),直接看代碼理解:

<!-- 非父子組件通信--中央事件總線 -->
<body>
    <div id="app">
        <p>{{message}}</p>
        <my-component></my-component>
    </div>

    <script src = "https://unpkg.com/vue/dist/vue.min.js"></script>
    <script>
        var bus = new Vue();

        Vue.component('my-component',{
            template: '<div><button @click="handleEvent"">傳遞事件</button></div>',
            
            methods: {
                handleEvent: function() {
                    bus.$emit('on-message', '來自組件my-component的內(nèi)容');
                },
            }
        });

        var app = new Vue({
            el: '#app',
            data: {
               message: '',
            },
            mounted: function() {
                var _this = this;
                //在實(shí)例初始化時(shí),監(jiān)聽來自bus實(shí)例的事件  
                bus.$on('on-message', function(msg) {
                    _this.message = msg;
                });
            }
        })
    </script>
</body>

上述代碼中為了方便起見,我們還是以父子組件為例的,但目的是為了理解代碼中名為bus的空Vue實(shí)例。

bus實(shí)例中,沒有任何內(nèi)容,實(shí)例app初始化時(shí),會(huì)在生命周期鉤子函數(shù)mounted中監(jiān)聽來自bus的事件on-message,而組件my-component中的按鈕點(diǎn)擊將on-message事件通過bus傳遞了給了app。

如果深入使用,可以擴(kuò)展bus實(shí)例,為他添加data、methods、computed等選項(xiàng),這些都是公用的,在實(shí)際業(yè)務(wù)中,比如用戶登錄的昵稱、性別、郵箱等通用信息,只需要在初始化時(shí)被bus獲取一次,則之后其他組件就可以直接使用了。

4. 父鏈 & 子組件索引

除了中央事件總線bus外,還有兩種方法可以實(shí)現(xiàn)組件之間的通信,父鏈子組件索引

**父鏈 **

在子組件中,使用this.$parent可以直接訪問該組件的父實(shí)例,父組件也可以通過this.$children訪問它的所有子組件,從而完成遞歸向上或向下的訪問:

<!-- 父鏈 -->
<body>
    <div id="app">
        <p>{{message}}</p>
        <my-component></my-component>
    </div>

    <script src = "https://unpkg.com/vue/dist/vue.min.js"></script>
    <script>
        Vue.component('my-component',{
            template: '<div><button @click="handleEvent"">通過父鏈直接修改父組件數(shù)據(jù)</button></div>',
            
            methods: {
                handleEvent: function() {
                    //訪問到父鏈并修改其數(shù)據(jù)
                    this.$parent.message = '來自子組件的修改數(shù)據(jù)';
                },
            }
        });

        var app = new Vue({
            el: '#app',
            data: {
               message: '我本來的數(shù)據(jù)',
            },
        })
    </script>
</body>  
執(zhí)行結(jié)果

在業(yè)務(wù)中,應(yīng)該盡可能避免子組件依賴父組件中的數(shù)據(jù),更不應(yīng)該主動(dòng)修改它的數(shù)據(jù),這不滿足父子組件之間解耦的原則。

理想情況下,只有組件自己可以修改自己的狀態(tài)。父子組件最好還是通過props$emit來完成通信。

子組件索引

當(dāng)子組件較多時(shí),通過this.$children來遍歷出所需要的子組件時(shí)比較困難的,因此Vue提供了子組件索引的方法,用特殊的屬性ref來為子組件指定一個(gè)索引名稱:

<!-- 子組件索引 -->
<body>
    <div id="app">
        <div>{{message}}</div>
        <button @click="handleRef">通過ref獲取子組件實(shí)例</button>
        <my-component ref="myComponent"></my-component>
    </div>

    <script src = "https://unpkg.com/vue/dist/vue.min.js"></script>
    <script>
        Vue.component('my-component',{
            data: function() {
                return {
                    message: '子組件內(nèi)容'
                }
            }
        });

        var app = new Vue({
            el: '#app',
            data: function() {
                return {
                    message: ''
                }
            },

            methods: {
                handleRef: function() {
                    //通過$refs訪問指定子組件實(shí)例
                    var msg = this.$refs.myComponent.message;
                    this.message = msg;
                }
            }
        })
    </script>
</body>  

上述代碼,子組件標(biāo)簽上使用ref指定一個(gè)名稱,并在父組件內(nèi)通過this.$refs來訪問到相應(yīng)子組件。

$refs只在組件渲染完成后才填充,并且它時(shí)非響應(yīng)式的,僅僅作為直接訪問子組件的應(yīng)急方案,應(yīng)盡量避免使用。

參考

  1. 《Vue.js 實(shí)戰(zhàn)》
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 能工摹形,巧匠竊意。必三省吾身,萬不可怠惰因循。 foreword 這篇容納了我個(gè)人所知道的一些Vue 2.x組件...
    aichisuan閱讀 419評(píng)論 0 1
  • 父子組件通信 1、父子組件通過prop傳遞數(shù)據(jù) 父組件可以將一條數(shù)據(jù)傳遞給子組件,這條數(shù)據(jù)可以是動(dòng)態(tài)的,父組件的數(shù)...
    視覺派Pie閱讀 1,338評(píng)論 0 18
  • Vue組件通信 Vue組件關(guān)系可分為三大類: 父子組件 兄弟組件 跨級(jí)組件, 相應(yīng)的組件之間的通信也分類三大類: ...
    dino小恐龍閱讀 1,938評(píng)論 0 2
  • 背景 ??Vue是單頁面應(yīng)用,單頁面應(yīng)用又是由組件構(gòu)成,各個(gè)組件之間又互相關(guān)聯(lián),那么如何實(shí)現(xiàn)組件之間通信就顯得尤為...
    A鄭家慶閱讀 1,069評(píng)論 0 1
  • 十九血戀 生與死有時(shí)不是人類能夠控制,何況疾病的來臨并沒有太多的先兆,緣海回家后知道了父親的病重,父親是緣海生命中...
    易寫發(fā)閱讀 245評(píng)論 0 1

友情鏈接更多精彩內(nèi)容