Vue组件通信秘籍:掌握这10大绝招,轻松玩转组件间数据传递
1. props / $emit
父组件
<template>
<child-component :msg="parentMsg" @child-event="handleChildEvent"></child-component>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
data() {
return {
parentMsg: 'Message from parent'
};
},
methods: {
handleChildEvent(data) {
console.log('Received data from child:', data);
}
}
};
</script>
子组件
<template>
<button @click="sendToParent">Send to Parent</button>
</template>
<script>
export default {
props: ['msg'],
methods: {
sendToParent() {
this.$emit('child-event', 'Data from child');
}
}
};
</script>
2. attrs/attrs / attrs/listeners
祖先组件
<template>
<great-grandchild-component :foo="foo" :bar="bar" @baz="handleBaz"></great-grandchild-component>
</template>
<script>
import GreatGrandchildComponent from './GreatGrandchildComponent.vue';
export default {
components: {
GreatGrandchildComponent
},
data() {
return {
foo: 'Foo',
bar: 'Bar'
};
},
methods: {
handleBaz() {
console.log('Baz event received');
}
}
};
</script>
中间组件
<template>
<grandchild-component v-bind="$attrs" v-on="$listeners"></grandchild-component>
</template>
<script>
import GrandchildComponent from './GrandchildComponent.vue';
export default {
components: {
GrandchildComponent
}
};
</script>
最终子组件
<template>
<div>{{ foo }} {{ bar }}</div>
<button @click="$emit('baz')">Emit Baz</button>
</template>
<script>
export default {
props: ['foo', 'bar']
};
</script>
3. 父链 / 子链(parent/parent / parent/children)
在 Vue.js 中,$parent
和 $children
属性可以用于直接访问组件的父级和子级实例,从而进行组件间的通信。然而,这种方法并不推荐用于常规的组件通信,因为它破坏了组件的封装性,使得组件之间的耦合度过高。但在某些特定场景下,如果你确实需要使用这种方法,下面是一个简单的示例:
父组件
<template>
<div>
<h2>Parent Component</h2>
<child-component></child-component>
<button @click="sendMessageToChild">Send Message to Child</button>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
methods: {
sendMessageToChild() {
// 通过 $children 访问子组件并调用其方法
this.$children.forEach(child => {
if (child.$options.name === 'ChildComponent') {
child.receiveMessage('Hello from Parent!');
}
});
}
}
};
</script>
子组件
<template>
<div>
<h3>Child Component</h3>
<p>{{ messageFromParent }}</p>
</div>
</template>
<script>
export default {
name: 'ChildComponent',
data() {
return {
messageFromParent: ''
};
},
methods: {
receiveMessage(message) {
this.messageFromParent = message;
}
}
};
</script>
4. provide / inject
祖先组件
<template>
<div>
<child-component></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
provide() {
return {
theme: 'dark'
};
}
};
</script>
子组件
<template>
<div :class="theme">Child Component</div>
</template>
<script>
export default {
inject: ['theme']
};
</script>
5. Vuex
Store
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
}
}
});
组件中使用
<template>
<button @click="increment">Increment</button>
<p>Count: {{ count }}</p>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
export default {
computed: {
...mapState(['count'])
},
methods: {
...mapMutations(['increment'])
}
};
</script>
6. Event Bus
Event Bus
import Vue from 'vue';
export const EventBus = new Vue();
发送事件的组件
<template>
<button @click="sendEvent">Send Event</button>
</template>
<script>
import { EventBus } from './event-bus';
export default {
methods: {
sendEvent() {
EventBus.$emit('custom-event', 'Data to send');
}
}
};
</script>
接收事件的组件
<template>
<div>Listening for events...</div>
</template>
<script>
import { EventBus } from './event-bus';
export default {
created() {
EventBus.$on('custom-event', this.handleCustomEvent);
},
beforeDestroy() {
EventBus.$off('custom-event', this.handleCustomEvent);
},
methods: {
handleCustomEvent(data) {
console.log('Received data:', data);
}
}
};
</script>
7. Slot 插槽
父组件
<template>
<child-component>
<div slot="header">Header Content</div>
<div slot="footer">Footer Content</div>
</child-component>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
}
};
</script>
子组件
<template>
<div>
<slot name="header"></slot>
<main>Main Content</main>
<slot name="footer"></slot>
</div>
</template>
<script>
export default {
// ...
};
</script>
8. ref
父组件
<template>
<child-component ref="childRef"></child-component>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
mounted() {
console.log(this.$refs.childRef); // 访问子组件实例
}
};
</script>
子组件
<template>
<div>Child Component</div>
</template>
<script>
export default {
// ...
};
</script>
9. provide / inject 的替代方案:使用 context
虽然 provide
和 inject
已经是 Vue 的内置特性,但如果你想要通过 context
来实现类似的功能,可以考虑使用 Vue 的混入(mixins)。
Mixin
export const InjectionMixin = {
created() {
if (this.$options.contextProvided) {
this.$context = this.$options.contextProvided;
}
}
};
提供 context 的组件
<template>
<div>
<child-component></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
import InjectionMixin from './InjectionMixin';
export default {
components: {
ChildComponent
},
mixins: [InjectionMixin],
data() {
return {
contextData: 'Some data'
};
},
provide() {
return {
contextProvided: this
};
}
};
</script>
接收 context 的组件
<template>
<div>{{ contextData }}</div>
</template>
<script>
import InjectionMixin from './InjectionMixin';
export default {
mixins: [InjectionMixin],
computed: {
contextData() {
return this.$context.contextData;
}
}
};
</script>
10. 全局混入实现组件通信
全局混入
Vue.mixin({
created() {
const eventBus = this.$root.$eventBus = new Vue();
Object.defineProperty(this, '$eventBus', {
get() {
return eventBus;
}
});
}
});
发送事件的组件
<template>
<button @click="sendEvent">Send Event</button>
</template>
<script>
export default {
methods: {
sendEvent() {
this.$eventBus.$emit('global-event', 'Data to send');
}
}
};
</script>
接收事件的组件
<template>
<div>Listening for global events...</div>
</template>
<script>
export default {
created() {
this.$eventBus.$on('global-event', this.handleGlobalEvent);
},
beforeDestroy() {
this.$eventBus.$off('global-event', this.handleGlobalEvent);
},
methods: {
handleGlobalEvent(data) {
console.log('Received global data:', data);
}
}
};
</script>
10. Vuex 状态管理
Vuex 是 Vue.js 的状态管理模式和库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
安装 Vuex
npm install vuex --save
创建 Vuex Store
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
}
},
actions: {
incrementAction({ commit }) {
commit('increment');
}
}
});
在 Vue 应用中使用 Vuex
import Vue from 'vue';
import App from './App.vue';
import store from './store';
new Vue({
store,
render: h => h(App)
}).$mount('#app');
组件中使用 Vuex
<template>
<div>
<p>{{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script>
export default {
computed: {
count() {
return this.$store.state.count;
}
},
methods: {
increment() {
this.$store.dispatch('incrementAction');
}
}
};
</script>
这些是 Vue.js 中实现组件通信的一些常见方法。选择哪种方法取决于你的具体需求和项目的结构。在大型项目中,Vuex 通常是一个很好的选择,因为它提供了一个结构化的方式来管理状态。在小型项目中,你可能只需要使用 props 和 events,或者 event bus 就足够了。
转载自:https://juejin.cn/post/7346245690001178664