https://www.cnblogs.com/raind/p/10241720.html
https://blog.csdn.net/infotw/article/details/91374480
技术没有好与坏,只有合适不合适
问题
为什么被移除
- 没有解决兄弟组件的通信问题
- 事件流动的方式是基于组件树结构的,当业务越来越烦杂时会混乱到难以维护
为什么使用
- 解决在父子层嵌套组件
- 通过$dispatch和broadcast定向的向某个父或者子组件远程调用事件,这样就避免了通过传props或者使用refs调用组件实例方法的操作。
代码
// Vue
/**
* Recursively broadcast an event to all children instances.
* 递归地向所有子实例广播事件。
* @param {String|Object} event
* @param {...*} additional arguments
*/
// $dispatch 方法是定义在 Vue 的 prototype 上的
// 接受一个事件
Vue.prototype.$broadcast = function (event) {
// 获取传入事件的类型,判断是否为字符串
var isSource = typeof event === 'string'
// 校正 event 的值,当接受 event 的类型为字符串时就直接使用,如果不是字符串就使用 event 上的 name 属性
event = isSource ? event : event.name
// if no child has registered for this event,
// then there's no need to broadcast.
// 如果当前组件的子组件没有注册该事件,就直接返回,并不用 broadcast
if (!this._eventsCount[event]) return
// 获取当前组件的子组件
var children = this.$children
// 将函数接受的参数转换成数组
var args = toArray(arguments)
// 如果传入事件为字符串
if (isSource) {
// use object event to indicate non-source emit
// on children
// 根据传入的事件名称的参数组装成 object
args[0] = { name: event, source: this }
}
// 循环子组件
for (var i = 0, l = children.length; i < l; i++) {
var child = children[i]
// 在每个子组件中调用 $emit 触发事件
var shouldPropagate = child.$emit.apply(child, args)
// 判断调用 $emit 返回的值是否为 true
if (shouldPropagate) {
// 如果调用 $emit 返回的值为 true,就递归孙子组件继续广播
child.$broadcast.apply(child, args)
}
}
// 最后返回当前组件的实例
return this
}
// element-ui
/**
* 派发和广播
* 核心思想是通过递归或遍历来查找要broadcast或dispatch的组件名字,然后在组件自身上emit与on
* @param {*} componentName 需要触发事件的组件名称
* @param {*} eventName 将要触发的事件名称
* @param {*} params 回调函数传递的参数
*/
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);
}
}
};
// 使用
// parent.vue
<template>
<div>
<h1>我是父组件</h1>
<button @click="handleClick">触发事件</button> <child />
</div>
</template>
<script>
import Emitter from "@/mixins/emitter.js";
import Child from "./child";
export default {
name: "componentA",
mixins: [Emitter],
created() {
this.$on("child-to-p", this.handleChild);
},
methods: {
handleClick() {
this.broadcast("componentB", "on-message", "Hello Vue.js");
},
handleChild(val) {
alert(val);
}
},
components: {
Child
}
};
</script>
// child.vue
<template>
<div>我是子组件</div>
</template>
<script>
import Emitter from "@/mixins/emitter.js";
export default {
name: "componentB",
mixins: [Emitter],
created() {
this.$on("on-message", this.showMessage);
this.dispatch("componentA", "child-to-p", "hello parent");
},
methods: {
showMessage(text) {
window.alert(text);
}
}
};
</script>