【入门级实例讲解下】Vue 2 组件间通信

559 阅读3分钟

前言

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。捞一下【入门级实例讲解上】Vue 2 父子组件通信 - 掘金 (juejin.cn),通过上篇保姆级实例讲解,想必各位有所收获,今天继续整活儿,用超详细的实例演示让你进一步掌握组件间的通信,声明一哈子,这可不仅是父子间传递数据!如果没看到上篇的话,建议看完之后再食用yo~

巩固

  1. 父向子组件传递数据通过 props,有静态/动态传值之分,区别就是是否通过 v-bind 绑定。

  2. ref 是给元素或子组件注册引用信息,通过调用子组件实例中的方法进行数据通信。

  3. 子向父传递数据通过 $emit 事件,子定义事件、传递参数(消息),父通过绑定事件来获取消息。

准备代码

准备三个组件,在父组件中引入两个子组件,并在各自组件之中定义一些后面要用到的数据属性。

  • Dad.vue 父组件
<template>
    <div class="dadBox">
        <hr>
        <p style="color: orangered">消息接收:{{ msg }}</p>
        <h2 style="color: brown;">我是父组件</h2>
        <broA></broA>
        <broB></broB>
    </div>
</template>

<script>
    import broA from './BroA.vue';
    import broB from './BroB.vue';

    export default {
        name: "Dad",
        components: { broA, broB },
        data() {
            return {
                msg: '空'
            }
        }
    }
</script>
  • BroA.vue 子组件A,和组件B互为兄弟组件
<template>
    <div class="baBox">
        <hr>
        <h4 style="color: blue;">我是兄弟组件 A</h4>
        <p>props 中的 message:{{ message }}</p>
    </div>
</template>

<script>
    export default {
        name: "BroA",
        props: {
            message: {
                type: String,
                default: '默认值'
            }
        },
        data() {
            return {
                content: '我是来自 BroA 组件的消息!'
            }
        }
    }
</script>
  • BroB.vue 子组件B,和组件A互为兄弟组件
<template>
    <div class="bbBox">
        <hr>
        <h4 style="color: red">我是兄弟组件 B</h4>
        <p>消息接收:{{ msg }}</p>
        <p>props 中的 message:{{ message }}</p>
    </div>
</template>

<script>
    export default {
        name: "BroB",
        props: {
            message: {
                type: String,
                default: '默认值'
            }
        },
        data() {
            return {
                msg: '空',
                content: '我是来自 BroB 组件的消息!'
            }
        }
    }
</script>

父子间通信【补充】

$parent

  • 在父组件中定义一个并没有什么卵用的方法,为方便后面演示在子中成功拿到父实例。
// Dad.vue
methods: {
    uselessMethod() {
        console.log('我是 Dad.vue 组件中一个方法!');
    }
}
  • 子组件通过 $parent 获取父组件实例,并调用其中的方法。
// BroA.vue / BroB.vue
created() {
    console.log('父组件实例:', this.$parent);
    this.$parent.uselessMethod();
}
  • 控制台

image.png

这里通过父链 $parent 成功在子组件中拿到父组件实例,达到了从父到子的数据通信。

$children

  • 在子组件中定义一个并没有什么卵用的方法,为方便后面演示在父中成功拿到子实例数组。
// BroA.vue / BroB.vue
methods: {
    uselessMethod() {
        console.log('我是 BroA/BroB 子组件中的一个方法!');
    }
}
  • 父组件通过 $children 获得子组件实例数组,并通过迭代调用每个子实例中的方法。
// Dad.vue
created() {
    console.log('子组件实例:', this.$children);
    this.$nextTick(() => {
        this.$children.forEach((child, index) => {
            console.log(`第 ${index+1} 个子组件实例:`, child);
            child.uselessMethod();
        });
    });

}
  • 控制台

image.png

这里通过子链 $children 成功在父组件中拿到子组件实例数组,达到了从子到父的数据通信。

以上实例演示了父/子链解决父子组件间的通信问题,它们之间的通信是相互的,并不是单向的。

兄弟组件之间通信

以父组件作为中转实现

  • 首先,在兄弟组件 B/A 都行,使用 props 自定义属性和绑定触发事件,实现父子之间的相互传值通信。
// BroB.vue
<div class="bbBox">
    <button @click="sendMessage">[兄弟组件通信演示]Send message by BroB</button>
</div>

methods: {
    sendMessage() {
        this.$emit('messageEvent', this.content);
    }
}
  • 其次,父组件拿到兄弟组件 B/A 传递的消息,作为一个中间人(桥梁)的角色将消息通过兄弟组件使用 props 暴露出来属性发送给另一个兄弟组件。
// Dad.vue
<!--将保存起来的消息取出去,父组件向子组件传值通过 props 暴露出来的属性完成-->
<broA :message="msg"></broA>
<broB @messageEvent="getMessage" :message="msg"></broB>

methods: {
    // 父组件接收到兄弟组件 B 定义的 messageEvent 事件传递过来的参数(消息)
    getMessage(message) {
        // 将接收到子组件的消息放到当前实例变量中保存起来
        this.msg = message;
    }
}

效果演示

效果演示.gif

暴露 Vue 实例的方式实现

  • 编写 JS 文件暴露出 Vue 实例。
import Vue from 'vue'

// 暴露出 Vue 实例
export default new Vue()
  • 在互相之间需要通信的组件中引入 JS 文件,引入时是 import xxx from '文件路径'
/*
    ☆ import ... from '...' 和 import {...} from '...' 的区别?

    注意:
        export default ... 的方式,需要通过 import 任意取名 from '文件路径'
        错误示范:import {任意取名} from '文件路径'

        export const xx = {
            ...
            ...
            ...
        }
        export function xxx() {
            ...
            ...
            ...
        }
        ...

        对于非默认导出,可以导入所需变量、函数...(按需引入)
        正确示例:import {xx, xxx, ...} from '文件路径'
        错误示例:import { 任意取名(在源文件中并没有定义) } from '源文件路径'

        如果需要用到源文件中的所有模块,则通过 import 真.任意取名 from '源文件路径'
        错误示例:import {真.任意取名} from '源文件路径'
 */
  • 选择一个组件作为消息发送源,使用 xxx.$emit('监听事件名', '发送的消息内容');
// BroA.vue
<button @click="sendMessage">[bus.js 方式通信演示]Send message by BroA</button>
methods: {
    sendMessage() {
        // 定义监听事件,传递参数,参数为消息内容,实现发送消息
        Bus.$emit('messageEvent', this.content);
    }
}
  • 组件接收消息,使用 xxx.$on('对应的监听事件名', callback); 从回调函数中拿到发送的消息。
mounted() {
    // 通过子组件 A 定义的监听事件拿到发送的消息
    Bus.$on('messageEvent', (message) => {
        // 放到当前实例变量中保存起来
        this.msg = message;
    });
}

效果演示

效果演示.gif

跨级通信

要点

  • 父组件使用 provide 注入数据,注入后可以提供给父组件下的所有子组件。
// Dad.vue
provide() {
    return {
        providedData: '我是你 Dad (^U^)ノ~YO'
    }
}
  • 子组件使用 inject 取出数据,不过要注意取出的变量名要和注入时的变量名保持一致。
// BroA.vue / BroB.vue
<p>子组件 BroA 接收父组件 provide 的信息:{{ providedData }}</p>

inject: ['providedData']

效果演示

image.png

总结

☆ 父子通信

  1. 父向子组件传递数据通过 props

  2. 子向父传递数据通过 $emit 事件

  3. 父链/子链使用 $parent/$children

  4. 直接访问组件实例用 $refs

☆ 兄弟通信

  1. 外部 JS 文件暴露 Vue 实例

  2. Vuex 状态管理

☆ 跨级通信

  1. provide/inject 父组件只管提供消息,子组件只负责接收

  2. 外部 JS 文件暴露 Vue 实例

  3. Vuex 状态管理

结尾

撰文不易,欢迎大家点赞、评论,你的关注、点赞是我坚持的不懈动力,感谢大家能够看到这里!Peace & Love。