源码
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: {
dispatch(componentName, eventName, params) {
var parent = this.$parent || this.$root;
var name = parent.$options.componentName;
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);
}
}
};
解析
- 核心思想是通过递归或遍历来查找要 broadcast 或 dispatch 的组件的名字,然后在组件自身上 emit 与 on
- emitter.js 中定义了两个函数:
broadcast(componentName, eventName, params)广播给子组件(向子组件方向传递),也就是不停的去遍历子节点并且通过$emit去触发事件,直到所有的子节点遍历完成之后停止dispatch(componentName, eventName, params)分发给父组件(向父组件方向传递),也就是不停的向上去遍历父节点,并通过$emit去除法事件,到达根节点之后停止
- 这两个函数在组件中使用的非常多,所以很多组件中都定义了 "componentName" 自定义属性,便于广播和分发
dispatch
dispatch(componentName, eventName, params) {} 方法有三个参数
componentName表示组件名称,用于匹配到正确的组件名eventName事件名,需要触发的事件params参数,触发时带入的参数
var parent = this.$parent || this.$root;
var name = parent.$options.componentName;
前两行(上面)用于查找到当前元素的父组件,如果没有就使用根节点,并取父组件的组件名用于后期匹配
// 遍历父组件,查找并匹配 name,直到找到对应的父组件或无
while (parent && (!name || name !== componentName)) {
parent = parent.$parent;
if (parent) {
// 匹配到,则修改 name,用于判断并退出循环
name = parent.$options.componentName;
}
}
紧接着是一个 while 循环,用于循环父组件,直到找到,或者到达根元素匹配不到。
在循环中不断使用 parent 变量记录组件名,用于判断是否找到。
其中的 if(parent) 是当有匹配到的父组件时,就去触发父组件的对应的事件函数。
broadcast
broadcast(componentName, eventName, params) {
broadcast.call(this, componentName, eventName, params);
}
.call可以改变this的指向,这里是对 this 进行了纠正,因为 broadcast 函数的 this 并不指向调用者
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]));
}
});
}
- 每条向下传播的线路,只要触发了一次对应的事件,就不会再向下传递了