前端基础之《Vue(14)—组件通信》

一、什么是组件通信

1、通信是组件或模块之间的数据交互。

2、组件多重通信就形成了数据流,数据流管理的优劣决定了产品能否上线,返工是否频繁的问题。

3、Vue中有哪些常见的通信方案?

组件树的概念:

在Vue中,组件树(Component Tree)指的是一个由多个嵌套的Vue组件组成的层级结构。每个组件可以是另一个组件的子组件,这样层层嵌套形成一棵树。这种结构允许开发者构建复杂和可重用的用户界面。

(1)父子组件通信

父传子使用自定义属性(使用props接收),子传父使用自定义事件(使用$emit)。

(2)状态提升

状态就是数据的意思。

当兄弟组件之间需要共享数据时,我们通常的做法是把这个数据定义在它们的共同的父组件中,再通过自定义属性实现数据共享。

(3)provide/inject通信

这是一种在组件树间自上而下的一种数据通信方案。

也就是说,只能从父级组件中向后代组件传递。

是Vue提供的两个内置选项,可以对象写法,也可以function写法。

需要注意的是,当provide提供动态数据(声明式变量)时,动态数据发生变化,后代组件们不会自动更新,这是为什么呢?

因为生命周期流程中,是在beforeCreate()和created()之间,inject注入provide数据。

所以下面在例子代码里,更新父组件中的provide数据,子组件不会更新!!!

(4)ref通信

ref是Vue内置的一个属性,就像id、class一样,每一个HTML元素或组件都有这个属性。

ref作用在HTML元素上,得到DOM实例,ref作用在组件上得到组件实例。

使用ref访问组件实例,可以进一步访问组件中的数据和方法。

ref是一种快捷的DOM访问方式,当然ref也可以作用在组件上得到组件实例对象。这些ref得到的DOM实例或组件实例,使用this.$refs来访问它们。

ref尽量少用,除非某些难搞的需求。

(5)slot插槽通信

借助于<slot>组件实现从子组件向父组件传递数据。

借助this.$slots访问父组件中的插槽实例。

(6)parent/children通信

借助parent/children这两个API,可以实现在任一组件中访问组件树中的其它任意组件实例。可以做到在组件中随意穿梭。

(7)attrs/listeners通信

借助$attrs可以访问父组件传递过来的自定义属性(除了class和style外)。

借助$listeners可以访问父组件给的自定义事件。

在某些场景下,attrs/listeners可以替代props/$emit()这种通用的通信方式。

(8)事件总线

借助Vue内置的事件系统。(on/emit/off/once)实现"订阅-发布"模式的通信。这种通信方式是一对多的,且与组件的层级无关。

(9)Vuex通信

这是Vue架构中终极的通信方案,也是Vue架构中用的最多的一种通信方式。

二、状态提升例子

有一个变量大哥点了加1,二哥点了减1。

html 复制代码
<html>
<head>
    <title>组件通信-状态提升</title>
    <style>
        
    </style>
</head>
<body>
    <div id="app">
        <comp-child-a :numa="num" @add="num+=$event"></comp-child-a>
        <hr>
        <comp-child-b :numb="num" @sub="num-=$event"></comp-child-b>
        <hr>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>

    <script>

        Vue.component('comp-child-a', {
            template: `
            <div>
                <div>大哥  {{numa}}</div>
                <button @click='$emit("add", 1)'>加1</button>
            </div>
            `,
            props: {
                numa: {type: Number, default: 0}
            }
        })

        Vue.component('comp-child-b', {
            template: `
            <div>
                <div>二哥  {{numb}}</div>
                <button @click='$emit("sub", 1)'>减1</button>
            </div>
            `,
            props: {
                numb: {type: Number, default: 0}
            }
        })

        const app = new Vue({
            // 变量定义在父组件中
            data() {
                return {
                    num: 1
                }
            }
        })
        app.$mount('#app')

    </script>

</body>
</html>

三、provide/inject例子

注意:provide提供声明式变量numf,如果在父组件中修改了app.num的值,子组件中不会改变!!!

html 复制代码
<html>
<head>
    <title>组件通信-provide/inject</title>
    <style>
        
    </style>
</head>
<body>
    <div id="app">
        <comp-child-c></comp-child-c>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>

    <script>

        Vue.component('comp-child-c', {
            template: `
            <div>
                <div>三哥 {{msg}} - {{numf}}</div>
                <span v-for='i in list' v-text='i'></span>
            </div>
            `,
            // 从组件树的上下文中注入provide的数据
            inject: ['msg','list','numf']
        })

        const app = new Vue({
            // 变量定义在父组件中
            data() {
                return {
                    num: 1
                }
            },
            // 从当前组件节点开始,向后代组件们传递数据
            // 工作中更推荐function写法,因为可以用this
            // provide: {
            //     msg: "你好",
            //     list: [1,2,3,4]
            // }
            provide() {
                return {
                    msg: '你好',
                    list: [1,2,3,4],
                    numf: this.num // 动态数据

                }
            }
        })
        app.$mount('#app')

    </script>

</body>
</html>

四、ref例子

html 复制代码
<html>
<head>
    <title>组件通信-ref</title>
    <style>
        
    </style>
</head>
<body>
    <div id="app">
        <div ref='box'>普通HTML标签</div>
        <comp-child-d ref='dd'></comp-child-d>
        <button @click='handle'>测试ref操作</button>
        
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>

    <script>

        Vue.component('comp-child-d', {
            template: `
            <div>
                <div>四哥  {{age}}</div>
            </div>
            `,
            data() {
                return {
                    age: 10
                }
            },
            methods: {
                changeAge(arg) {
                    this.age = arg || 10
                }
            }
        })

        const app = new Vue({
            // 变量定义在父组件中
            data() {
                return {
                    num: 1
                }
            },
            methods: {
                handle() {
                    console.log('---refs', this.$refs)
                    this.$refs.box.style.color = 'red'
                    this.$refs.dd.changeAge(20)
                }
            }
        })
        app.$mount('#app')

    </script>

</body>
</html>

点击按钮:

获取到DOM实例和组件实例:

相关推荐
恋猫de小郭11 分钟前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60617 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅8 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment8 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊9 小时前
jwt介绍
前端