完全搞懂Vue組件間通訊的6種方式(建議收藏 !)

在vue的學習中,vue組件間的通訊是不得不瞭解的,在實際開發中,也是很是經常使用的,因此這裏我總結了Vue組件的通訊的6種方式,但願能幫助小夥伴們更好更快的去理解Vue組件間的通訊前端

cmd-markdown-logo

組件關係說明

cmd-markdown-logo

由上邊的圖能夠看出如下幾個關係:vue

  • A與B是父子關係
  • A與C是父子關係
  • B與D是父子關係
  • B與E是父子關係
  • B與C是兄弟關係
  • D與E是堂兄關係
  • A與D是隔代關係
  • A與E是隔代關係

由上邊幾個關係對下邊場景預覽:vuex

  • 父子 組件之間的數據傳遞
  • 兄弟 組件之間的數據傳遞
  • 祖先組件 與 子組件 之間的數據傳遞

下面來爲你們詳細講解實現這些關係的幾種通訊方式跨域

props/$emit

$parent / $children與 ref

$emit/$on

vuex

$attrs/$listeners

provide/inject

幾種組件通訊方法更好地選用markdown

當咱們的項目比較大時,能夠選擇更好的狀態管理解決方案vuex。

節制地使用$parent和$children,它們的主要目的是做爲訪問組件的應急方法,
更推薦用props和events實現父子組件通訊

若是僅僅是傳遞數據,就用$attrs/$listeners好點
如何不只傳遞數據,還作中間處理,就用vuex好些

方法一:props/$emit

  • 父組件A 向 子組件B 傳遞數據 經過props的方法
  • 子組件B 向 父組件A 發送數據 經過emit

父組件向子組件傳遞數據

下面經過一個例子來看父組件是如何向子組件傳遞數據的:
這個例子是子組件son.vue 經過 props 獲取 父組件father.vue 中的數據:
sonList: ['小白', '小紅', '小藍','小綠']app

  • father父組件
<template>
  <div class="father">
    <com-son :sons="sonList"></com-son>
  </div>
</template>

<script>
import comSon from './son'
export default {
  name: 'HelloWorld',
  components: { comSon },
  data() {
    return {
      sonList: ['小白', '小紅', '小藍','小綠']
    }
  }
}
</script>
  • 子組件 son.vue
<template>
  <div>
    <span v-for="(item, index) in sons" :key="index">{{item}}</span>
  </div>
</template>

<script>
export default {
  props: ['sons']
}
</script>

注意:異步

props 只能夠從上一級組件傳遞到下一級組件,也就是父子組件,即這就是單向數據流ide

props是隻讀,不能夠被修改,全部被修改都會失效和被警告函數

子組件向父組件傳遞數據

下面經過一個例子來看子組件是如何向父組件傳遞值:這個例子是子組件son.vue經過$emit向父組件值的傳遞學習

  • 父組件father.vue
<template>
  <div class="father">
    <com-son :sons="sonList" @onEmitIndex="onEmitIndex"></com-son>
    <p>{{currentIndex}}</p>
  </div>
</template>

<script>
import comSon from './son'
export default {
  name: 'HelloWorld',
  components: { comSon },
  data() {
    return {
      currentIndex: -1,
      sonList: ['小白', '小紅', '小藍','小綠']
    }
  },
  methods:{
    onEmitIndex(idx){
      this.currentIndex = idx
    }
  }
}
</script>
  • 子組件son.vue
<template>
  <div>
    <span v-for="(item, index) in sons" :key="index" @click="emitIndex(index)">{{item}}</span>
  </div>
</template>

<script>
export default {
  props: ['sons'],
  methods: {
    emitIndex(index){
      this.$emit('onEmitIndex',index)
    }
  }
}
</script>

方法二:$parent/$children與ref

  • 子實例能夠用this.$parent訪問父實例
  • 子實例被推入父實例的$children
  • ref:若是在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;若是用在子組件上,引用就指向組件實例

在這裏我要說一下:

節制地使用$parent和$children,它們的主要目的是做爲訪問組件的應急方法,

更推薦用props和events實現父子組件通訊

下面咱們來經過一個實例說明$parent和$children的用法:

  • 父組件father.vue
<template>
  <div class="father">
    <com-son></com-son>
    <button @click="name">點擊改變子組件的值</button>
  </div>
</template>

<script>
import comSon from './son'
export default {
  name: 'HelloWorld',
  components: { comSon },
  data() {
    return {
      msg: 'hello,早上好!'
    }
  },
  methods:{
    name(){
      this.$children[0].message = "hello"
    }
  }
}
</script>
  • 子組件son.vue
<template>
  <div class="com_a">
    <span>{{message}}</span>
    <p>獲取父組件的值:{{parentVal}}</p>
  </div>
</template>

<script>
export default {
  data(){
    return {
      message:'Good wether'
    }
  },
  computed:{
    parentVal(){
      return this.$parent.msg;
    }
  }
}
</script>

下邊再說ref訪問組件的例子:

  • 子組件
export default {
  data () {
    return {
      title: 'Vue.js'
    }
  },
  methods: {
    sayHello () {
      window.alert('Hello');
    }
  }
}
  • 父組件
<template>
  <component-a ref="comA"></component-a>
</template>
<script>
  export default {
    mounted () {
      const comA = this.$refs.comA;
      console.log(comA.title);
      comA.sayHello();
    }
  }
</script>

注意:

這兩種方法的弊端,沒法在跨域兄弟間通訊

$emit/$on

這個方法可用於父子、隔代、兄弟組件通訊

這種方式是經過一個相似App.vue的實例做爲一個模塊的事件中心,用它來觸發事件和監聽事件,從而實現任何組件間的通訊,包括父子、隔代、兄弟組件

當項目比較大的時候,能夠選擇更好的狀態管理解決方案vuex

下邊咱們來經過一個實例說明emit/on的用法:

在這裏先說一下:

有兩個組件A、B,在B組件中接收到A組件傳過來的數據

首先開闢個新的Vue根實例

而後咱們在A組件中經過$emit方式去定義一個自定義事件方法

而後經過$on去接收A組件自定義的事件傳過來的值

  • 首先建立一個vue的空白實例
import Vue from 'vue'
export default new Vue()
  • 子組件A
把組件A經過$emit傳到那個Vue空白實例裏面
<template>
    <div>
        <span>A組件->{{msg}}</span>
        <input type="button" value="把a組件數據傳給b" @click ="send">
    </div>
</template>
<script>
import vmson from "util/emptyVue"
export default {
    data(){
        return {
            msg:{
                a:'666',
                b:'999'
            }
        }
    },
    methods:{
        send:function(){
            vmson.$emit("aevent",this.msg)
        }
    }
}
</script>
  • 子組件B
組件B經過$on去監聽vmson實例中的自定義方法aevent
<template>
 <div>
    <span>{{msg}}</span>
 </div>
</template>
<script>
      import vmson from "util/emptyVue"
      export default {
         data(){
                return {
                    msg:""
                }
            },
         mounted(){
                vmson.$on("aevent",(val)=>{//監聽aevent事件
                    console.log(val);//打印出來結果
                    this.msg = val;
                })
          }
    }
</script>
  • 父組件
這個父組件就是把A、B兩個組件放在父組件中註冊渲染
<template>
     <div>
      <childa></childa>
      <childb></childb>    
     </div>
</template>
<script>
   import childa from './childa.vue';
   import childb from './childb.vue';
   export default {
    components:{
        childa,
        childb
    },
    data(){
        return {
            msg:""
        }
    },
    methods:{
       
    }
   }
</script>

Vuex

Vuex是什麼?

Vuex是一個專爲Vue.js應用程序開發的狀態管理模式,它解決了組件之間同一狀態的共享問題,它採用集中式存儲管理應用的全部組件的狀態,因此組件就能夠和stort通信了,其實Vuex就是用來管理組件之間通訊的一個組件

爲何要使用Vuex?

假如不使用vuex

  • 父子組件依賴同一個state

cmd-markdown-logo

  • 兄弟組件依賴同一個state

cmd-markdown-logo

用了Vuex以後
cmd-markdown-logo

Vuex各個模塊

state

state中存放頁面共享的狀態字段

getters

至關於當前模塊state的計算屬性

mutations

若是想更新state中的字段,提交mutations中定義的事件的惟一的方式
(key爲事件名,value是一個函數),可是中國事件函數必須是同步執行的

actions

能夠定義異步函數,並在回調中提交mutation,就至關於異步更新了state中的字段

modules

相似於命名空間,用於項目中將各個模塊的狀態分開定義和操做,便於維護

Vuex實例應用

  • 父組件
<template>
  <div id="app">
    <ChildA/>
    <ChildB/>
  </div>
</template>

<script>
  import ChildA from 'components/ChildA'
  import ChildB from 'components/ChildB'

  export default {
    name: 'App',
    components: {ChildA, ChildB}
  }
</script>
  • 子組件ChildA
<template>
  <div id="childA">
    <h1>我是A組件</h1>
    <button @click="transform">點我讓B組件接收到數據</button>
    <p>{BMessage}}</p>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        AMessage: 'Hello,B組件,我是A組件'
      }
    },
    computed: {
      BMessage() {
        return this.$store.state.BMsg
      }
    },
    methods: {
      transform() {
        this.$store.commit('receiveAMsg', {
          AMsg: this.AMessage
        })
      }
    }
  }
</script>
  • 子組件ChildB
<template>
  <div id="childB">
    <h1>我是B組件</h1>
    <button @click="transform">點我看A組件接收的數據</button>
    <p>{{AMessage}}</p>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        BMessage: 'Hello,A組件,我是B組件'
      }
    },
    computed: {
      AMessage() {
        return this.$store.state.AMsg
      }
    },
    methods: {
      transform() {
        this.$store.commit('receiveBMsg', {
          BMsg: this.BMessage
        })
      }
    }
  }
</script>
  • vuex模塊store.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const state = {
  // 初始化A和B組件的數據,等待獲取
  AMsg: '',
  BMsg: ''
}

const mutations = {
  receiveAMsg(state, payload) {
    state.AMsg = payload.AMsg
  },
  receiveBMsg(state, payload) {
    state.BMsg = payload.BMsg
  }
}

export default new Vuex.Store({
  state,
  mutations
})

$attrs/$listeners

用在父組件傳遞數據給子組件或者孫組件
若是僅僅是傳遞數據,就用$attrs/$listeners好點

如何不只傳遞數據,還作中間處理,就用vuex好些

$attrs:

$attrs繼承全部的父組件屬性(除了prop傳遞的屬性、class和style)

當一個組件沒有聲明任何 prop 時,這裏會包含全部父做用域的綁定 ( class 和 style 除外 ),而且能夠經過 v-bind="$attrs" 傳入內部組件。一般配合 inheritAttrs 選項一塊兒使用。

$listeners:

它是一個對象,包含了父做用域中的v-on事件監聽器,能夠配合v-on="$listeners"將全部的事件監聽器指向這個組件的某個特定的子元素

下面咱們來經過一個實例看$attrs/$listeners的用法:

例:假設有三個組件:A組件包含B組件,B組件包含C組件

  • A組件
<template>
    <div id="app">
        <child1 :p-child1="child1" :p-child2="child2" v-on:test1="onTest1" 
        v-on:test2="onTest2"
        //此處監聽了兩個事件,能夠在B組件或者C組件中直接觸發
        >
        </child1>
    </div>
</template>
<script>
import Child1 from './Child1.vue';
export default {
    data() {
        return {};
    },
    components: { Child1 },
    methods: {
        onTest1() {
            console.log('test1 running...');
        },
        onTest2() {
            console.log('test2 running');
        }
    }
};
</script>
  • B組件
<template>
    <div class="child-1">
        <p>in child1:</p>
        <p>props: {{pChild1}}</p>
        <p>$attrs: {{$attrs}}</p>
        <hr>
        <child2 v-bind="$attrs" v-on="$listeners"></child2>
            //v-on綁定了$listeners,因此C組件能直接觸發test
            //v-bind綁定了$attrs,因此C組件能夠獲取A組件傳遞下來的props的值
    </div>
</template>
<script>
import Child2 from './Child2.vue';
export default {
    props: ['pChild1'],
    data() {
        return {};
    },
    inheritAttrs: false,
    components: { Child2 },
    mounted() {
        this.$emit('test1');
    }
};
</script>
  • C組件
<template>
    <div class="child-2">
        <p>in child2:</p>
        <p>props: {{pChild2}}</p>
        <p>$attrs: {{$attrs}}</p>
        <hr>
    </div>
</template>
<script>
export default {
    props: ['pChild2'],
    data() {
        return {};
    },
    inheritAttrs: false,
    mounted() {
        this.$emit('test2');
    }
};
</script>

provide/inject

祖先組件中經過provider來提供變量,而後在孫組件中經過inject來注入變量

procide/inject API主要解決了跨域組件間的通信問題,不過它的使用場景,主要是子組件獲取上級組件的狀態,跨級組件間創建了一種主動提供與依賴注入的關係

下邊經過一個例子來講明provide/inject的用法:

  • 父組件
<template>
    <div>
        <son prop="data"></son>
    </div>
</template>
 
<script>
export default {
    provide: {
        name: 'Tom'
    }
}
  • 孫子組件
這裏的孫子組件指的是:父組件、子組件、孫子組件
<template>
    <div>
        {{name}}
    </div>
</template>
 
<script>
export default {
    name: 'grandson',
    inject: [name]
}
</script>

這裏能夠經過inject直接訪問其兩個層次以上的數據,

用法與props徹底相同

總結

vue組件間的通信大體能夠分爲三類

父子通信

props/emit、parent/children、 attrs/$listeners、provide/inject API、ref

父向子傳遞數據經過props

子向父傳遞是經過$emit、event

子實例訪問父實例經過$parent

父實例訪問子實例經過$children

$attrs用父組件傳遞數據給子組件或孫組件
(包含了父做用域中不做爲 prop 被識別 (且獲取) 的特性綁定 (class 和 style 除外))

listeners用父組件傳遞數據給子組件或孫組件
包含了父做用域中的 (不含 .native 修飾器的) v-on 事件監聽器

祖先組件經過provider提供變量給子孫組件

子孫組件經過inject注入變量給祖先組件

ref用來訪問組件實例

兄弟通訊

Vuex

vuex用來做爲兄弟之間和跨級之間的通訊

跨級通訊

Vuex、attrs/listeners、provide/inject API

vuex用來做爲兄弟之間和跨級之間的通訊

$attrs用父組件傳遞數據給子組件或孫組件
(包含了父做用域中不做爲 prop 被識別 (且獲取) 的特性綁定 (class 和 style 除外))

listeners用父組件傳遞數據給子組件或孫組件
包含了父做用域中的 (不含 .native 修飾器的) v-on 事件監聽器

祖先組件經過provider提供變量給子孫組件

子孫組件經過inject注入變量給祖先組件

最後

若是本文對你有幫助得話,給本文點個贊❤️❤️❤️

歡迎你們加入,一塊兒學習前端,共同進步!
cmd-markdown-logo
cmd-markdown-logo

相關文章
相關標籤/搜索