Vue 父子组件通信的三种方案

解决方法


  • 使用props进行通信
  • 使用事件分发机制(EventBus)
  • $parent 属性
  • vuex方式 (后续补充)

结构图
这里写图片描述

一、 props p r o p s 属性


ref 被用来给子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。

props 是在注册组件的时候进行定义,要保证props中的属性值和ref中的值是对应的。

实现原理

通过在子组件props中定义自定义属性值,然后在父组件ref中引入该自定义属性值,并通过$refs对象传递给子组件。这样就保证了由父组件到子组件的通信。
这里写图片描述

实现源码

下面的代码演示了,父组件(parent)分别给两个子组件(child),进行传值的过程。

父组件
parent.vue

<template>

    <div class="container"> 
        <Child1 :context="obj.context"></Child1>
        <Child2 :staticImgUrl="obj.url"></Child2>
    </div>
</template>

import Child1 from './child1'  // 引入子组件
import Child2 from './child2'  // 引入子组件



export default {
    name: 'parent',
    components: {
        Child1       // 添加子组件1
        Child2       // 添加子组件2
    },
    data() {    // 父组件的数据项
        return {
            obj: {    //所有数据封装在obj对象中,便于管理
                url: 'abc.jpg',
                context:'abcdefg'
            }
        };
    }
    methods: {}
}

子组件
child1.vue

<template>
    <div class="child">
        <span>{{ context }}</span>
    </div>
</template>

export default {
    name: 'child',
    components: {},
    props:[   // 绑定子组件自身的属性数据

        'context', // 文本
    ],
    data() {
        return {};
    }
    methods: {}
}

child2.vue

<template>
    <div class="child">
        <img src="staticImgUrl" />
    </div>
</template>

export default {
    name: 'child',
    components: {},
    props:[   // 绑定子组件自身的属性数据

        'staticImgUrl', // 静态图片url
    ],
    data() {
        return {};
    }
    methods: {}
}

问题一 这种方式是单向通信的(局限性)

使用第一种方式的话,只能由父组件向子组件进行单向传值。子组件是无法满足向兄弟节点或者父节点进行直接通信。
一旦子组件的值发生改变,是无法修改父组件中的值的。

应用场景
父组件是一个容器,有两个子组件分别是表格(iTable)和分页器(iPage),当选中不同对页码时,要对表格内容进行更新。这种情况下使用ref/props就比较鸡肋了。

那么解决方案就是采用第二种方式(EventEmitter)

问题二 子组件修改prop值

所有的prop都是由父组件单向向下绑定的,当父级prop得到更新的时候,子组件的所有prop都将刷新为最新的值。因此,不应该在子组件内部修改prop的值,否则,浏览器的控制台会发出警告!!!

二、 EventBus E v e n t B u s


通过使用事件中心,允许组件自由交流,无论组件处于组件树的哪一层。由于 Vue 实例实现了一个事件分发接口,你可以通过实例化一个空的 Vue 实例来实现这个目的。

dispatch-和-broadcast-替换

这些方法的最常见用途之一是父子组件的相互通信。

这种方式也就是设计模式中的观察者模式,通过在父组件上注册监听$on事件,当子组件要发消息给父组件的时候,直接发送$emit事件,然后,父组件做相应的操作。
这里写图片描述

bus.js

let bus = new Vue
export default bus // 创建一个Vue实例进行事件分发

parent

<template>
    <div class="parent">
    </div>
</template>
export default {
    name: 'parent',
    components: {},
    mounted: () {
        // 默认监听函数放在mounted中
        // 监听子节点的notify
        let self = this
        this.$on('display', (evt) => {
            // 通知给子节点2
            self.$emit('displaySuccess')
        })
    },
    data() {
        return {};
    }
    methods: {
    }
}

child1


<template>
    <div class="child" @click="dispatch">
    </div>
</template>
export default {
    name: 'child1',
    components: {},
    data() {
        return {};
    }
    methods: {
        dispatch () {
            this.$emit('display')
        }
    }
}

child2


<template>
    <div class="child2">
    </div>
</template>
export default {
    name: 'child2',
    components: {},
    mounted(){
        //监听父节点的notify
        this.$on('displaySuccess', (res) => {
            // opts
            ...
        })
    },
    data() {
        return {};
    }
    methods: {
    }
}

三、 $parent/ $ p a r e n t / $refs $ r e f s 属性

Vue 提供了一种可以从子组件访问父组件的实例$parent
相反,父组件可以访问子组件的实例$refs

$parent 和 $refs 本质上都是组件实例,因此我们可以通过实例对象访问组件内部的方法。

//parent
<template>
<Comp ref="child"></Comp>
</template>
name: 'parent',
components:{
    Comp // 引入子组件
},
methods: {
  notifyChild: function () {
    this.$refs.child.focus() // this.$refs就是Comp组件实例
  }
}

...

// child
name: 'Comp',
methods: {
  focus: function () {
      console.log('调用子组件',this.$parent) // this.$parent就是parent组件实例

  }
}

四、 vuex v u e x 方式

上述两种方式基本满足日常开发需求,这种方式之后稍后会更新。
待续…

这里写图片描述

Logo

前往低代码交流专区

更多推荐