承接文章 Vue2中10种组件通信方式和实践技巧,因为一篇文章太长无法发表,所以做拆分。
在 Vue@1 版本中,有 $dispatch
和 $broadcast
这种基于组件树的工作流来通信。缺点是项目变复杂后,结构扩展的过程中会变得越来越难维护,所以被废弃了。
本质上还是 $on()
和 $emit()
监听和触发事件。
关键点:
$emit()
触发的是实例自己通过$on()
监听的事件。
1,dispatch
是向任意指定的祖先组件通信。
实现思路:先在指定的祖先组件中使用 this.$on()
监听事件 eventParent。后代组件需要触发祖先组件监听的事件 eventParent 时,层层向上找到指定的祖先组件,通过 $emit()
触发监听的事件。
2,broadcast
是向任意指定的后代组件通信。
实现思路,先在指定的后代组件中使用 this.$on()
监听事件 event1。父组件需要触发后代组件监听的事件 event1 时,会递归遍历子组件找到指定的后代组件,通过 $emit()
触发监听的事件。
element-ui 的实现
element-ui 是基于自己的考虑来使用的,比如表单组件的传参验证等,基于组件树但层级不深并且可控。
// 因为会被递归调用,所以写到外面
function broadcast(componentName, eventName, params) {
this.$children.forEach((child) => {
var name = child.$options.componentName; // 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; // 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);
},
},
};
A<–B<–C(A是祖先组件)
// 触发祖先组件 A 的 eventParent 事件,并传参 123
this.dispatch('A', 'eventParent', 123)
举例:
<template>
<BComponent />
template>
<script>
import BComponent from "./components/B.vue";
export default {
componentName: "A",
components: {
BComponent,
},
methods: {
methodParent(item) {
console.log(item);
},
},
created() {
this.$on("eventParent", this.methodParent);
},
};
script>
<template>
<CComponent />
template>
<script>
import CComponent from "./C.vue";
export default {
components: {
CComponent,
},
};
script>
<template>
<button @click="handleClick">触发祖先组件A的方法button>
template>
<script>
import Emitter from "@/mixins/emitter.js";
export default {
mixins: [Emitter],
methods: {
handleClick() {
this.dispatch("A", "eventParent", 123);
},
},
};
script>
A<–B<–C(A是祖先组件)
// 触发后代组件 C 的 eventChild 事件,并传参 123
this.broadcast('C', 'eventChild', 123)
举例,
<template>
<div>
<BComponent />
<button @click="handleClick">触发后台组件C的方法button>
div>
template>
<script>
import BComponent from './components/B.vue'
import Emitter from '@/mixins/emitter.js'
export default {
components: {
BComponent
},
mixins: [Emitter],
methods: {
handleClick() {
this.broadcast('C', 'eventChild', 123) // 会触发 eventChild 事件绑定的2个处理函数。
}
}
}
script>
<template>
<CComponent />
template>
<script>
import CComponent from './C.vue'
export default {
components: {
CComponent
}
}
script>
<template>template>
<script>
export default {
componentName: 'C',
methods: {
methodC1(item) {
console.log(item)
},
methodC2(item) {
console.log(item)
}
},
created() {
this.$on('eventChild', this.methodC1)
this.$on('eventChild', this.methodC2)
}
}
script>
以上。