Vue組件通信

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

組件通信場景

可以將其分為父子組件通信兄弟組件通信跨級組件通信

1. 自定義事件

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

<!-- 自定義事件 -->
<body>
    <div id="app">
        <p>SUM: {{total}} </p>
        <!-- 父組件用v-on監聽子組件事件,做出處理 -->
        <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觸發increase事件,第一個參數為自定義事件的名稱,第二個參數是要傳遞的數據
                    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>  
執行結果

子組件有兩個按鈕,分別實現+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>  

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

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

v-model還可以用來創建自定義的表單輸入組件,進行數據雙向綁定。

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

在Vue 2.x中,推薦使用一個空的Vue實例作為中央事件總線(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的內容');
                },
            }
        });

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

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

bus實例中,沒有任何內容,實例app初始化時,會在生命周期鉤子函數mounted中監聽來自bus的事件on-message,而組件my-component中的按鈕點擊將on-message事件通過bus傳遞了給了app。

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

4. 父鏈 & 子組件索引

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

**父鏈 **

在子組件中,使用this.$parent可以直接訪問該組件的父實例,父組件也可以通過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"">通過父鏈直接修改父組件數據</button></div>',
            
            methods: {
                handleEvent: function() {
                    //訪問到父鏈并修改其數據
                    this.$parent.message = '來自子組件的修改數據';
                },
            }
        });

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

在業務中,應該盡可能避免子組件依賴父組件中的數據,更不應該主動修改它的數據,這不滿足父子組件之間解耦的原則。

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

子組件索引

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

<!-- 子組件索引 -->
<body>
    <div id="app">
        <div>{{message}}</div>
        <button @click="handleRef">通過ref獲取子組件實例</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: '子組件內容'
                }
            }
        });

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

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

上述代碼,子組件標簽上使用ref指定一個名稱,并在父組件內通過this.$refs來訪問到相應子組件。

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

參考

  1. 《Vue.js 實戰》
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,750評論 2 370

推薦閱讀更多精彩內容

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