vue组件间的通信

本文详细介绍了Vue组件之间的通信方式,包括父子组件通信(props、$emit、$refs)、非父子组件通信(事件总线EventBus、Vuex)以及跨级通信的各种策略。通过props向子组件传递数据,子组件通过$emit向父组件发送事件。EventBus作为通信桥梁,适用于简单场景,大型项目推荐使用Vuex进行状态管理。此外,还提到了$attrs和$listeners在组件通信中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

组件通信可以分为:

  1. 父子组件之间通信
  2. 非父子组件之间通信(兄弟组件、隔代关系组件等)

props / $emit

父子组件通信最常用的是父组件通过props的方式向子组件传递数据,而子组件通过$emit可以向父组件通信。

props

prop 只可以从上一级组件传递到下一级组件(父子组件),即所谓的单向数据流。而且 prop 只读,不可被修改,所有修改都会失效并警告。如果prop是对象则可以修改其属性值。

// 父组件
<template>
    <Child message="Hello,child!"></Child>
</template>
<script>
import Child from './child'
export default {
    components: {
        Child
    }
}
</script>
//子组件
<template>
    <div>{{message}}</div>
</template>
<script>
export default {
    props: ['message']
}
</script>
$emit

子组件通过$emit触发一个自定义事件,并将要传递数据作为$emit的参数,父组件通过v-on监听这个事件并接收参数。

// 父组件
<template>
    <div>
        <Child @sendMessage="reciveMessage"></Child>
        {{text}}
    </div>
</template>
<script>
import Child from "./child";
export default {
    components: {
        Child
    },
    methods: {
        reciveMessage(message) {
            this.text = message;
        }
    },
    data() {
        return {
            text: ""
        };
    }
};
</script>
//子组件
<template>
    <button @click="postmessage">click</button>
</template>
<script>
export default {
    methods: {
        postmessage(){
            this.$emit('sendMessage', 'Hi,parent!');
        }
    }
}
</script>

$children / $parent

子组件通过$parent可以访问父组件的实例,父组件通过$children可以访问所有子组件的实例。$parent是一个对象,$children是一个数组。需要注意 $children 并不保证顺序,也不是响应式的。

//父组件
<template>
    <div>
        <Child></Child>
        <button @click="changeChild">change</button>
    </div>
</template>
<script>
import Child from "./child";
export default {
    components: {
        Child
    },
    data() {
        return {
            text: "Parent's text~"
        };
    },
    methods: {
        changeChild() {
            this.$children[0].text = "Parent change";
        }
    }
};
</script>
//子组件
<template>
    <div>
        <p>{{parentText}}</p>
        <p>{{text}}</p>
    </div>
</template>
<script>
export default {
    computed: {
        parentText() {
            return this.$parent.text;
        }
    },
    data() {
        return {
            text: `Child's text`
        };
    }
};
</script>

provide/ inject

父组件中通过provide来提供变量, 然后再子组件中通过inject来注入变量。不论子组件嵌套有多深, 只要调用了inject 那么就可以注入provide中的数据。

// 父组件
<template>
    <div>
        <child></child>
    </div>
</template>
<script>
import child from "./child";
export default {
    components: {
        child
    },
    provide: {
        text: 'parent'
    }
};
</script>
//子组件
<template>
    <div>
        <grandchild></grandchild>
        Child get message: {{this.text}}
    </div>
</template>
<script>
import grandchild from "./grandchild";
export default {
    components: {
        grandchild
    },
    inject: ['text']
};
</script>
//孙子组件
<template>
    <div>Grandchild get message: {{this.text}}</div>
</template>
<script>
export default {
    inject: ['text']
};
</script>

ref / refs

ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。ref 如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例。

v-for 用于元素或组件的时候,引用信息将是包含 DOM 节点或组件实例的数组。

因为 ref 本身是作为渲染结果被创建的,在初始渲染的时候你不能访问它们,$refs 也不是响应式的,因此你不应该试图用它在模板中做数据绑定。

//父组件
<template>
    <div>
        <child ref="child"></child>
        <button @click="change">change</button>
    </div>
</template>
<script>
import child from "./child";
export default {
    components: {
        child
    },
    methods: {
        change(){
            this.$refs.child.text = 'parent change'
        }
    }
};
</script>
//子组件
<template>
    <div>{{text}}</div>
</template>
<script>
export default {
    data(){
        return {
            text: `child's text`
        }
    }
};
</script>

事件总线 EventBus

EventBus 又称为事件总线,可以使用它来作为沟通的桥梁,所有组件都可以向该中心触发事件或监听事件。

EventBus也有不方便之处,当项目较大,就容易造成难以维护的灾难。

// EventBus.js
import Vue from 'vue'
export const EventBus = new Vue()
//父组件
<template>
    <div>
        <child1></child1>
        <child2></child2>
    </div>
</template>
<script>
import child1 from "./child";
import child2 from "./child2";
export default {
    components: {
        child1,
        child2
    }
};
</script>
// 子组件child1
<template>
    <div>
        <button @click="send">child1</button>
    </div>
</template>
<script>
import {EventBus} from './EventBus.js'
export default {
    methods: {
        send(){
            EventBus.$emit('send','Hello,bro!');
        }
    }
};
</script>
// 子组件child2
<template>
    <div>{{text}}</div>
</template>
<script>
import {EventBus} from './EventBus.js'
export default {
    mounted() {
        EventBus.$on("send", message => this.text = message);
    },
    data() {
        return {
            text: "im child2"
        };
    }
};
</script>

如果想移除事件的监听, 可以像下面这样操作:

EventBus.$off(event, callback);

参数:

  1. eventstring | Array<string>
  2. callbackFunction

用法:

  • 如果没有提供参数,则移除所有的事件监听器;

  • 如果只提供了事件,则移除该事件所有的监听器;

  • 如果同时提供了事件与回调,则只移除这个回调的监听器。

Vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
Vuex 有几个核心概念:

  1. state:用于数据的存储,是store中的唯一数据源。在state存放的数据是响应式的,数据发生改变,依赖这个数据的组件也会发生更新。
  2. getters:可以认为是state的计算属性,基于state数据进行二次包装。
  3. mutations:改变state数据的唯一途径,但不能用于处理异步事件,需要使用store.commit()[kəˈmɪt]触发。它会接受 state 作为第一个参数。
  4. actions:用来改变state数据,内部也是运用了mutation,可以包含任意异步操作,通过 store.dispatch() 方法触发。
  5. modulesmodules 允许我们将 store 分割成模块(module)。每个模块拥有自己的 statemutationactiongetter、甚至是嵌套子模块。
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
    state: {
        text: 'hello'
    },
    mutations: {
        changeText(state, newText){
            state.text = newText;
        }
    }
})
//父组件
<template>
    <div>
        <child></child>
        <button @click="change">parentchangetext</button>
    </div>
</template>
<script>
import child from "./child";
export default {
    components: {
        child
    },
    methods: {
        change(){
            this.$store.commit('changeText', 'im parent');
        }
    }
};
</script>
<template>
    <div>Child: {{this.$store.state.text}}</div>
</template>
<script>
export default {

};
</script>

$attrs$listeners

$attrs 包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (classstyle 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (classstyle 除外),并且可以通过 v-bind="$attrs" 传入内部组件。
$listeners 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件。

//父组件
<template>
    <div>
        <child 
            message1="hi,grandchild!" 
            message2="hello,grandchild!" 
            @message="sayhi"
        ></child>
    </div>
</template>
<script>
import child from "./child";
export default {
    components: {
        child
    },
    methods: {
        sayhi(message) {
            console.log(message);
        }
    }
};
</script>
//子组件
<template>
    <div>
        <grandchild v-bind="$attrs" v-on="$listeners"></grandchild>
        {{message1}}
    </div>
</template>
<script>
import grandchild from "./grandchild";
export default {
    components: {
        grandchild
    },
    props: ['message1'],
    inheritAttrs: false
};
</script>
//孙子组件
<template>
    <div>
        <p>{{message2}}</p>
        <button @click="postmessage">click</button>
    </div>
</template>
<script>
export default {
    props: ["message2"],
    methods: {
        postmessage(){
            this.$emit('message', 'hello,grandparent');
        }
    }
};
</script>

总结

常见使用场景可以分为三类:

父子组件通信: props / $emit$parent / $childrenprovide / injectref / $refsEventBus,Vuex
兄弟组件通信: EventBus,Vuex
跨级通信: EventBus,Vuex,provide / inject$attrs / $listeners

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值