Vue 組件通信

組件作為Vue中的核心概念,是值得我們深入研究的課題之一,通過研究它,我們可以理解更高深的思想,可以提升自己的開發(fā)技巧。而今天,我要討論的是Vue的組件通信。
眾所周知,組件通信是通過props和emit去完成的,但實(shí)際上,這只是眾多方式中的一種而已。而且針對(duì)不同的情況,會(huì)有更合適的方法。下面就聽我慢慢道來。

1.props和emit

父組件:
<template>
<div class="parent-box">
      <h3>我是父元素,props方式</h3>
      <p class="content">
        通過$emit獲得子元素屬性{{children2}}
      </p>
      <Children2 @changeChild2="changeChild2"></Children2>
</div>
</template>

<script>
data(){
    return {
      children2:'children2',
    }
  },
  methods:{
    changeChild2(val){
      this.children2 = val
    },
}
</script>

子組件
<template>
    <div class="children-box">
        <h4>我是子元素</h4>
        <button @click="clickEvent">改值</button>
        <p>通過props通信 {{value}}</p>
    </div>
</template>
<script>
    export default {
        props:['value'],
        methods:{
            clickEvent(){
                // 核心代碼
                this.$emit('changeChild2',Math.random())
            }
        }
    }
</script>

大體效果如下:


20190407_211309.gif

可以看到,通過點(diǎn)擊按鈕,可以改變通過props傳入子組件的value屬性。因?yàn)檫@種方式是大家最常用的一種方式,這里就不做詳細(xì)解釋了。

2.$parent$children

$parent 屬性可以用來從一個(gè)子組件訪問父組件的實(shí)例。它提供了一種機(jī)會(huì),可以在后期隨時(shí)觸達(dá)父級(jí)組件,以替代將數(shù)據(jù)以 prop 的方式傳入子組件的方式。
$children可以訪問當(dāng)前實(shí)例的直接子組件。
下面來看一個(gè)例子,代碼如下,注意注釋部分。

父組件
<template>
<div class="parent-box">
      <h3>我是父元素,$parent,$children方式</h3>
      <p class="content">
        通過$children獲得子元素屬性{{children1}}
      </p>
      <Children1></Children1>
</div>
</template>
<script>
data(){
    return {
      parent1:'parent1',
      children1:'children1',
    }
  },
 mounted() {
    // 核心代碼,通過$children獲取子組件的屬性
    this.children1 = this.$children[0]._data.value
  },
</script>
子組件
<template>
    <div class="children-box">
        <h4>我是子元素</h4>
        <input type="text" v-model="parent">
        <p>通過$parent獲取父元素的屬性 {{parent}}</p>
    </div>
</template>
<script>
    export default {
        data(){
            return {
                // 核心代碼,通過$parent獲得父組件的屬性
                parent:this.$parent._data.parent1
            }
        },
        mounted() {
        },
        watch:{
            parent(val){
                // 核心代碼,改變父組件中的屬性
                this.$parent._data.parent1 = val
            }
        }
    }
</script>

大體效果如下:


20190407_212944.gif

可以看到,通過this.$parent._data.parent1 = val,改變子組件中parent的值,然后賦值給父組件的parent1可以直接改變父組件的屬性值。
雖然這種方式比較方便快捷,但有很大的副作用,就如官網(wǎng)所說:

在絕大多數(shù)情況下,觸達(dá)父級(jí)組件會(huì)使得你的應(yīng)用更難調(diào)試和理解,尤其是當(dāng)你變更了父級(jí)組件的數(shù)據(jù)的時(shí)候。當(dāng)我們稍后回看那個(gè)組件的時(shí)候,很難找出那個(gè)變更是從哪里發(fā)起的。

節(jié)制地使用 $parent$children - 它們的主要目的是作為訪問組件的應(yīng)急方法。更推薦用 props 和 events 實(shí)現(xiàn)父子組件通信

3.總線方式

有時(shí)候,我們的組件并不止父子關(guān)系這么簡(jiǎn)單,可能兄弟組件之間也要進(jìn)行通信,而EventBus就能解決這個(gè)問題,相對(duì)于vuex它更輕量,不需要我們引入vuex這個(gè)龐然大物,更加適合小型項(xiàng)目。
我們?cè)谥鲗?shí)例App之外,單獨(dú)定義一個(gè)空的Bus實(shí)例,來進(jìn)行組件間的通信。
下面來看一個(gè)例子,代碼如下

bus.js
// 核心代碼
import Vue from 'vue'
var Bus = new Vue()
export default Bus

父組件:
<template>
<div class="parent-box">
      <h3>我是父元素,總線方式</h3>
      <Children31></Children31>
      <Children32></Children32>
</div>
</template>

子組件1
<template>
    <div class="children-box">
        <h4>我是子元素</h4>
        <p>通過總線方式通信 {{msg}}</p>
    </div>
</template>
<script>
    import Bus from '../bus'
    export default {
        data(){
            return {
                msg:'hello world'
            }
        },
        mounted() {
        },
        created(){
              // 核心代碼,接受事件
            Bus.$on('setMsg',val=>{
                this.msg = val
            })
        },
    }
</script>
子組件2
<template>
    <div class="children-box">
        <h4>我是子元素</h4>
        <input type="text" v-model="msg">
        <p>通過總線方式通信 {{msg}}</p>

    </div>
</template>
<script>
    import Bus from '../bus'
    export default {
        data(){
            return {
                msg:'hello world'
            }
        },
        mounted() {
        },
        watch:{
            msg:function (newVal) {
                // 核心代碼發(fā)出事件
                Bus.$emit('setMsg',newVal)
            },
        }
    }
</script>

大體效果如下:


20190407_215821.gif

可以看到,我改變子組件2 input的值會(huì)觸發(fā)emit事件,去改變子組件1中的msg。

4.$attrs$listeners

$attrs$listeners是2.4.0才新加入的方法,用來解決組件的跨級(jí)傳輸非常有用。試想有A、B、C三個(gè)組件,A包含B,B包含C,如果我想在A上給C傳參,并且接收C的事件怎么辦呢?
原先,我們只使用pros去傳參的話,就只能拿B作為中轉(zhuǎn)組件,B組件定義足夠多的pros,不僅僅用于自身,還要用于傳輸給C,而事件的傳遞,也只能一層層地往上傳,這樣就會(huì)使代碼很繁瑣,臃腫,不利于維護(hù)。
$attrs$listeners就是用來處理這種情況的,代碼如下

父組件A
<template>
<div class="parent-box">
      <h3>我是父元素A,$attrs,$listeners方式</h3>
      <Children4 :value1="value1" :value2="value2" @clickEvent1="clickEvent1" @clickEvent2="clickEvent2"></Children4>
</div>
</template>
<script>
data(){
      value1:'B',
      value2:'C',
},
methods:{
clickEvent1(){
      this.value1 = Math.random()
    },
    clickEvent2(){
      this.value2 = Math.random()
    }
}
</script>
子組件B
<template>
    <div class="children-box">
        <h4>我是子元素B</h4>
        <p>{{value1}}</p>
        <button @click="clickEvent">改變value1的值</button>
        <Children42 v-bind="$attrs" v-on="$listeners"></Children42>
    </div>
</template>
<script>
    import Children42 from './Children4.2'
    export default {
        name:'Children41',
        inheritAttrs:false,
        props:['value1'],
        components:{
            Children42
        },
        methods:{
            clickEvent(){
                this.$emit('clickEvent1')
            }
        },
    }
</script>
子組件C
<template>
    <div class="children-box">
        <h4>我是子元素C</h4>
        <p>{{value2}}</p>
        <button @click="clickEvent">改變value1的值</button>
    </div>
</template>
<script>
    export default {
        name:'Children42',
        inheritAttrs:false,
        props:['value2'],
        data(){
            return {
            }
        },
        mounted() {
        },
        methods:{
            clickEvent(){
                this.$emit('clickEvent2')
            }
        },
    }
</script>

大體效果如下:


20190407_221354.gif

可以看到,我們只要在引用C組件的時(shí)候,加入v-bind="$attrs" v-on="$listeners"兩個(gè)屬性即可,這樣,C組件就可以接收到來自A組件的值,A組件也能接收到來自C組件的事件。
如此以來,就不需要在B組件定義中轉(zhuǎn)的屬性和方法,如果你的組件結(jié)構(gòu)比較復(fù)雜,這種方式可以很大程度減少代碼的冗余,更加的輕量化。

5.provide和inject

這對(duì)選項(xiàng)需要一起使用,以允許一個(gè)祖先組件向其所有子孫后代注入一個(gè)依賴,不論組件層次有多深,并在起上下游關(guān)系成立的時(shí)間里始終生效。如果你熟悉 React,這與 React 的上下文特性很相似。
這相對(duì)于attrs和listeners可能更加簡(jiǎn)介,只需要父組件提供變量,子組件注入就行,不需要在中間組件寫什么代碼,但并不推薦在業(yè)務(wù)代碼中使用,正如官方所說。

provide 和 inject 主要為高階插件/組件庫(kù)提供用例。并不推薦直接用于應(yīng)用程序代碼中。

因?yàn)閜rovide inject 會(huì)有一個(gè)類似冒泡的特性,數(shù)據(jù)源有可能在中間被”“打斷”,甚至是有可能被組件庫(kù)中的組件打斷,或者打斷組件庫(kù)中的provide,不利于維護(hù)

代碼如下:

父組件
<template>
<div class="parent-box">
      <h3>我是父元素,provide,inject方式</h3>
      <Children5></Children5>
    </div>
</template>
<script>
data(){
return {
     theme:'blue'
},
// 核心代碼
provide(){
    return {
      test:this
    }
},
}
</script>

子組件
<template>
    <div class="children-box">
        <h4 :style="{color:value.theme}">我是子元素</h4>
        <div @click="changeValue">改顏色</div>
    </div>
</template>
<script>
    export default {
        // 核心代碼
        inject: {
            value:{
                from:'test',
                default:()=>{}
            }
        },
        methods:{
            changeValue(){
                this.value.theme = 'red'
            }
        }
    }
</script>

大體效果如下:


20190407_223227.gif

我們?cè)诟附M件中提供一個(gè)test屬性,然后賦值為this,這里之所以賦值this,是為了讓provide和inject的綁定變成可響應(yīng)的,這樣,我再子組件中就可以直接改變父組件的theme屬性。

6. Vuex

Vuex 是一個(gè)專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式。它采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測(cè)的方式發(fā)生變化。
Vuex的功能強(qiáng)大,但應(yīng)對(duì)簡(jiǎn)單的組件通信用Vuex就顯得多余了,有種殺雞用牛刀的感覺,還會(huì)增加我們代碼的理解難度。
Vuex作為我們必須掌握的技能之一,這里也不再贅述,不了解的話,官網(wǎng)就是最好的學(xué)習(xí)材料。

總結(jié)

vue中組件通信的方式很多,應(yīng)對(duì)不同情況,靈活地采用最適合的方式,才能使我們的代碼變得優(yōu)雅。

以上是目前為止,我所知的所有通信方式,如有遺漏,歡迎補(bǔ)充。

以下,是代碼的demo地址
https://github.com/hanwolfxue/blog-demo-vue-communicate.git

?著作權(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),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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