Vue2.0父子組件間事件派發機制

從vue1.x過來的都知道,在vue2.0中,父子組件間事件通訊的$dispatch$broadcase被移除了。官方考慮是基於組件樹結構的事件流方式實在是讓人難以理解,而且在組件結構擴展的過程當中會變得愈來愈脆落。特別是在組件層級比較深的狀況下。經過廣播和事件分發的機制,就顯得比較混亂了。javascript

官方在廢除的同時,也爲咱們提供了替換方案,包括實例化一個空的vue實例,使用$emit反應子組件上的狀態變化html

1.使用$emit觸發事件

helloWorld.vue做爲父組件,dialogConfigVisible變量控制子組件彈框顯示或隱藏。
configBox.vue做爲子組件,假設爲封裝的公告彈窗。vue

在父組件中 helloWorld.vue 中java

< template/>git

<config-box
     :visible="dialogConfigVisible"                
      @listenToConfig="changeConfigVisible"
  > </config-box>

scriptgithub

data(){
    return {
      dialogConfigVisible:true
    }
  }
   methods: {
     changeConfigVisible(flag) {
         this.dialogConfigVisible = flag;
     }
   }

而後,在子組件 configBox.vue 中,主要在任意事件回調中,使用 $emit來觸發自定義的 listenToConfig事件,後面還能夠加上參數傳給父組件。好比,在子組件彈窗上點擊×關閉時,通知父組件 helloWorld.vue我要關閉了,主要方便父組件改變相應狀態變量,並傳入false到自定義的事件中。element-ui

scriptapp

methods:{
  dialogClose() {
    this.show = false;
    this.$emit("listenToConfig", false)
  }
}

在子組件中,主動觸發listenToConfig事件,並傳入參數 false, 告訴父組件 helloWorld.vue對話框要關閉了。這裏就能夠避免父組件中的狀態未變化,再次刷新頁面的時候對話框會自動出現。ide

2.實例化一個空的vue實例bus

這裏實例化一個bus 空vue實例,主要爲了統一管理子組件和父組件相互通訊,經過bus 做爲媒介,
首先新建一個bus.js 文件,在裏面新建一個對象,父組件爲table.vue, 子組件爲tableColumn.vueui

// bus.js
 import Vue from "vue";
 export var bus = new Vue({
     data:{
       scrollY:false
     },
     methods:{
        updateScrollY(flag){
          this.scrollY = flag;
        }
     }
   })

而後分別引入:

// table.vue
 <script>
  import {bus}  from "./bus"
   export default {
      created(){
        bus.$on('getData',(argsData)=>{
          // 這裏獲取子組件傳來的參數
          console.log(argsData);
          })

      }
   }

  </script>

  // tableColumn.vue
  <script>
    import {bus} from "./bus"
    export default{
      methods(){
        handleClick(){
          bus.$emit('getData',{data:"from tableColumn!"})
        }
      }
    }
  </script>

上面的父子組件中,父組件中利用bus註冊監聽事件getData,子組件中一旦有狀態變化,就觸發bus上對應的事件。

這種利用空實例的方式,至關於建立了一個事件中心,因此這種通訊一樣適用於非父子組件間的通訊,

3.多級父子組件通訊

有時,可能想要實現通訊的兩個組件不是直接的父子組件,而是祖父和孫子,或者是跨越了更多層級的父子組件

不可能由子組件一級一級的向上傳遞參數,來達到通訊的目的,雖然如今咱們理解的通訊都是這樣通過中轉的。能夠經過while等循環,不斷向上遍歷,直到找到目標父組件,就在對應的組件上觸發事件。

下面就只element-ui實現的一個父子組件通訊的mixins,對於組件同步有很大的做用。在element-ui 的優勢概述中也特地提到這個組件通訊

function broadcast(componentName, eventName, params) {

  // 向下遍歷每一個子節點,觸發相應的向下廣播的 事件
  this.$children.forEach(child => {
    var name = child.$options.componentName;

    if (name === componentName) {
      child.$emit.apply(child, [eventName].concat(params));
    } else {
      broadcast.apply(child, [componentName, eventName].concat([params]));
    }
  });
}
export default {
  methods: {
     // 向上遍歷父節點,來獲取指定父節點,經過$emit 在相應的 組件中觸發 eventName  事件
    dispatch(componentName, eventName, params) {
      var parent = this.$parent || this.$root;
      var name = parent.$options.componentName;
      // 上面的componentName 須要在每一個vue 實例中額外配置自定義屬性 componentName,
      //能夠簡單替換成var name = parent.$options._componentTag;

      while (parent && (!name || name !== componentName)) {
        parent = parent.$parent;

        if (parent) {
          name = parent.$options.componentName;
        }
      }
      if (parent) {
        parent.$emit.apply(parent, [eventName].concat(params));
      }
    },
    broadcast(componentName, eventName, params) {
      broadcast.call(this, componentName, eventName, params);
    }
  }
};

首先定義兩個嵌套的組件 f1.vue 和 c1.vue,實例是:

<f1>
   <c1></c1>
 </f1>

而後,分別定義兩個父子組件:

c2.vue

 <template>
     <section>
     <button type="button" name="button" @click="dispatchTest">點擊一下,就能夠</button>
   </section>
 </template>
<script type="text/javascript">
import Emitter from "../mixins/emitter";
export default {
name: "c2",
mixins: [Emitter],
componentName:'c2',
methods: {
  dispatchTest() {
    this.dispatch('f1', 'listenerToC1', false);
  }
}
}
</script>
f1.vue

<template type="html">
  <div class="outBox-class">
    <slot>
    </slot>
  </div>
</template>

<script type="text/javascript">
import Emitter from "../mixins/emitter";
export default {
name: "f1",
mixins: [Emitter],
componentName: 'f1',
mounted() {
  this.$on("listenerToC1", (value) => {
     alert(value);
  })
}
}
</script>

這樣,就能夠在子組件中點擊按鈕,觸發 listenerToC1事件,在父組件中監聽到這個事件,
其實更$emit觸發事件相似。不一樣之處在於,這裏能夠多級嵌套,不必定是直接的父子組件均可以觸發到。

4 .sync 修飾符

在Vue1.x中,利用prop進行"雙向綁定",實現父子組件通訊,都會用到.sync修飾符,能夠將子組件中對應的prop值變化同步到父組件中。可是,這樣就破壞了單向數據流,在2.0版本中被移除了,在2.3.0版本中又以一種語法糖的形式加了進來。
能夠看下文檔上給出的實例

<comp :foo.sync=「bar」></comp>

被擴展爲

<comp :foo="bar" @update:foo= "val =>bar=val"></comp>

其實跟本文中第一種方法基本一致,更加簡化了。

一樣helloWorld.vue做爲父組件, configBox.vue做爲子組件,

<config-box
     :visible.sync="dialogConfigVisible"                
  > </config-box>

而後在子組件中,顯式的觸發更新事件:

methods:{
  dialogClose() {
    this.show = false;
    this.$emit("update:visible", false)
  }
}

這樣visible 的變化就能同步到父組件中了。

相關文章
相關標籤/搜索