您的当前位置:首页正文

有关Vue2.0父子组件间实现派发机制(详细教程)

2020-11-27 来源:华拓网

本篇文章主要介绍了浅谈Vue2.0父子组件间事件派发机制,现在分享给大家,也给大家做个参考。

从vue1.x过来的都知道,在vue2.0中,父子组件间事件通信的$dispatch和$broadcase被移除了。官方考虑是基于组件树结构的事件流方式实在是让人难以理解,并且在组件结构扩展的过程中会变得越来越脆落。特别是在组件层级比较深的情况下。通过广播和事件分发的机制,就显得比较混乱了。

官方在废除的同时,也为我们提供了替换方案,包括实例化一个空的vue实例,使用$emit反应子组件上的状态变化

1.使用$emit触发事件

helloWorld.vue作为父组件,dialogConfigVisible变量控制子组件弹框显示或隐藏。

configBox.vue作为子组件,假设为封装的公告弹窗。

在父组件中 helloWorld.vue 中

< template/>

 <config-box
 :visible="dialogConfigVisible" 
 @listenToConfig="changeConfigVisible"
 > </config-box>

script

 data(){
 return {
 dialogConfigVisible:true
 }
 }
 methods: {
 changeConfigVisible(flag) {
 this.dialogConfigVisible = flag;
 }
 }

然后,在子组件 configBox.vue 中,主要在任意事件回调中,使用 $emit来触发自定义的 listenToConfig事件,后面还可以加上参数传给父组件。比如,在子组件弹窗上点击×关闭时,通知父组件 helloWorld.vue我要关闭了,主要方便父组件改变相应状态变量,并传入false到自定义的事件中。

script

methods:{
 dialogClose() {
 this.show = false;
 this.$emit("listenToConfig", false)
 }
}

在子組件中,主动触发listenToConfig事件,并传入参数 false, 告诉父组件 helloWorld.vue对话框要关闭了。这里就可以避免父组件中的状态未变化,再次刷新页面的时候对话框会自动出现。

2.实例化一个空的vue实例bus

这里实例化一个bus 空vue实例,主要为了统一管理子组件和父组件相互通信,通过bus 作为媒介,首先新建一个bus.js 文件,在里面新建一个对象,父组件为table.vue, 子组件为tableColumn.vue

 // 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上对应的事件。

这种利用空实例的方式,相当于创建了一个事件中心,所以这种通信同样适用于非父子组件间的通信,

3.多级父子组件通信

有时,可能想要实现通信的两个组件不是直接的父子组件,而是祖父和孙子,或者是跨越了更多层级的父子组件

不可能由子组件一级一级的向上传递参数,来达到通信的目的,虽然现在我们理解的通信都是这样经过中转的。可以通过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">
 <p class="outBox-class">
 <slot>
 </slot>
 </p>
</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触发事件类似。不同之处在于,这里可以多级嵌套,不一定是直接的父子组件都可以触发到。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

如何通过js将当前时间格式化?

使用vue引入css,less相关问题

通过vue如何引入公共css文件

在Vue中有关使用ajax方法有哪些?