从vue1.x过来的都知道,在vue2.0中,父子组件间事件通讯的$dispatch
和$broadcase
被移除了。官方考虑是基于组件树结构的事件流方式实在是让人难以理解,而且在组件结构扩展的过程当中会变得愈来愈脆落。特别是在组件层级比较深的状况下。经过广播和事件分发的机制,就显得比较混乱了。javascript
官方在废除的同时,也为咱们提供了替换方案,包括实例化一个空的vue实例,使用$emit反应子组件上的状态变化html
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
这里实例化一个bus 空vue实例,主要为了统一管理子组件和父组件相互通讯,经过bus 做为媒介,
首先新建一个bus.js 文件,在里面新建一个对象,父组件为table.vue
, 子组件为tableColumn.vue
ui
// 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
上对应的事件。
这种利用空实例的方式,至关于建立了一个事件中心,因此这种通讯一样适用于非父子组件间的通讯,
有时,可能想要实现通讯的两个组件不是直接的父子组件,而是祖父和孙子,或者是跨越了更多层级的父子组件
不可能由子组件一级一级的向上传递参数,来达到通讯的目的,虽然如今咱们理解的通讯都是这样通过中转的。能够经过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
触发事件相似。不一样之处在于,这里能够多级嵌套,不必定是直接的父子组件均可以触发到。
在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 的变化就能同步到父组件中了。