el's blog

A noder, FEer. use express, vue...etc

vue2.0版本的broadcat以及dispatch
众所周知,vue在2.0版本中移除了$broadcast以及$dispatch方法,而采用了$emit和$on来管理组件之间的事件,但某些复杂的业务场景下,这功能仍然有些受限。

问题场景

最近在业务中遇到一种情况,一个页面有若干子模块,子模块中用了一个弹窗组件,简化模型如下:

1
2
3
4
5
6
7
<grand-page>
<parent-form>
<el-modal></el-modal>
</parent-form>
<el-button>点击显示parent-form</el-button>
</grand-page>

问题来了,数据在最外层的 grand-page 拉取,再一层层传入,点击 el-button 后触发显示 parent-form ,而 parent-form 要显示又得显示 el-modal,如果要写两层 props 就传递一个 show 变量是否展示。

写起来麻烦不说,也很不智能,因为 vue2 中不允许直接改 props 的值,所以得写两层的 emit & on 来监听 show 变量。

还有一种方法是使用 .sync 修饰符,很可惜,这么做只能在父子组件间使用,但是从 grand-page 到 el-modal 这祖孙间在用一个 show 变量就行不通了。

于是就引申出了本篇的话题,想在祖和孙之间传数据,自然少不了 $broadcast 了,虽然在 vue2 中它被废弃了,但是我们可以通过语法糖来实现它。

原理

vue2 使用 $emit 和 $on 来传递并接收事件,并且在同一个组件里,就可以 $emit 某个事件让 $on 接收到。如果我们遍历当前组件所有子组件,也就是 this.$children ,并且在每个子组件里 $emit 一个事件,目标的子孙组件都可以使用 $on 来监听,就完成了 broadcat 的过程。

代码

摘取 Element 的 mixin 中的 emitter 关于这部分的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function broadcast(componentName, eventName, params) {
/*遍历当前节点下的所有子组件*/
this.$children.forEach((child) => {
/*获取子组件名称*/
var name = child.$options.componentName;
if (name === componentName) {
/*如果是我们需要广播到的子组件的时候调用$emit触发所需事件,在子组件中用$on监听*/
child.$emit.apply(child, [eventName].concat(params));
} else {
/*非所需子组件则递归遍历深层次子组件*/
broadcast.apply(child, [componentName, eventName].concat([params]));
}
});
}
export default {
methods: {
/*
向所有子组件进行事件广播。
这里包了一层,为了修改broadcast的this对象为当前Vue实例
*/
broadcast(componentName, eventName, params) {
broadcast.call(this, componentName, eventName, params);
}
}
};

接着祖元素调用

1
2
3
4
5
6
7
8
9
10
import Emitter from '@/mixins/emitter.js';
export default {
mixins: [Emitter],
methods: {
showTheForm () {
this.broadcast('parent-form', 'show');
}
}
};

子元素接收

1
2
3
4
5
6
7
export default {
created () {
this.$on('show', (val) => {
// ...
});
}
};

这么做的好处是子元素内部的数据可以不从父元素中 props ,使得子元素看起来结构能更加清晰。