相关文章
vue 通信方式
一、Vue3.x通信方式
Props / Events(属性 / 事件)
当使用 Props 和 Events 进行组件之间的通信时,通常是父组件通过 Props 向子组件传递数据,然后子组件通过触发事件向父组件传递信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import { defineComponent, ref } from 'vue'; import ChildComponent from './ChildComponent.jsx';
export default defineComponent({ setup() { const message = ref('Hello from Parent');
const handleClick = () => { message.value = 'Message updated by Parent'; };
return () => ( <div> <h2>Parent Component</h2> {/* 将数据和方法作为属性传递给子组件 */} <ChildComponent message={message.value} onUpdateMessage={handleClick} /> </div> ); } });
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import { defineComponent, ref } from 'vue';
export default defineComponent({ props: { message: String }, setup(props, { emit }) { const inputMessage = ref('');
const handleChange = (event) => { inputMessage.value = event.target.value; };
const handleClick = () => { emit('updateMessage', inputMessage.value); };
return () => ( <div> <h3>Child Component</h3> {/* 显示父组件传递过来的数据 */} <p>Message from Parent: {props.message}</p> {/* 输入框和按钮 */} <input type="text" v-model={inputMessage.value} onChange={handleChange} /> <button onClick={handleClick}>Update Message</button> </div> ); } });
|
Provide / Inject(提供 / 注入):
Provide / Inject 是 Vue 中一种高级的组件通信方式,允许祖先组件向所有后代组件注入数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import { defineComponent, provide } from 'vue'; import ChildComponent from './ChildComponent.jsx';
export default defineComponent({ setup() { provide('message', 'Hello from Parent');
return () => ( <div> <h2>Parent Component</h2> <ChildComponent /> </div> ); } });
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import { defineComponent, inject } from 'vue';
export default defineComponent({ setup() { const message = inject('message');
return () => ( <div> <h3>Child Component</h3> <p>Message from Parent: {message}</p> </div> ); } });
|
Pinia
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import { defineStore } from 'pinia';
export const useCounterStore = defineStore({ id: 'counter', state: () => ({ count: 0 }), actions: { increment() { this.count++; }, decrement() { this.count--; }, reset() { this.count = 0; } } });
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import { defineComponent } from 'vue'; import { createPinia } from 'pinia'; import { useCounterStore } from './store';
const pinia = createPinia();
export default defineComponent({ setup() { pinia.useStore(useCounterStore);
const counterStore = useCounterStore();
return () => ( <div> <h1>Pinia Counter App</h1> <p>Count: {counterStore.count}</p> <button onClick={counterStore.increment}>Increment</button> <button onClick={counterStore.decrement}>Decrement</button> <button onClick={counterStore.reset}>Reset</button> </div> ); } });
|
全局事件总线:
在 Vue 3 中使用全局事件总线的概念与 Vue 2 中类似,但是由于 Vue 3 不再支持 $on 和 $emit 这样的全局实例方法,我们需要借助一个 Vue 实例来作为事件总线。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import { createApp, ref } from 'vue';
const eventBusApp = createApp({}); const eventBus = ref(eventBusApp);
export const emitEvent = (eventName, eventData) => { eventBus.value.config.globalProperties.$emit(eventName, eventData); };
export const onEvent = (eventName, callback) => { eventBus.value.config.globalProperties.$on(eventName, callback); };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import { defineComponent } from 'vue'; import { onEvent, emitEvent } from './EventBus.jsx';
export default defineComponent({ setup() { onEvent('customEvent', (data) => { console.log('Received data in ComponentA:', data); });
const handleClick = () => { emitEvent('customEvent', 'Hello from ComponentA'); };
return () => ( <div> <h2>Component A</h2> <button onClick={handleClick}>Trigger Event</button> </div> ); } });
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import { defineComponent } from 'vue'; import { onEvent } from './EventBus.jsx';
export default defineComponent({ setup() { onEvent('customEvent', (data) => { console.log('Received data in ComponentB:', data); });
return () => ( <div> <h2>Component B</h2> <p>Check console for received event data</p> </div> ); } });
|
$attrs / $listeners:
可以在子组件中通过 $attrs 获取父组件传递的非 props 特性,通过 $listeners 获取父组件绑定的非原生事件监听器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import { defineComponent } from 'vue'; import ChildComponent from './ChildComponent.jsx';
export default defineComponent({ setup() { return () => ( <div> <h2>Parent Component</h2> {/* 在父组件中使用 $attrs 和 $listeners */} <ChildComponent message="Hello from parent" onClick={() => console.log('Click event in parent')} /> </div> ); } });
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import { defineComponent } from 'vue';
export default defineComponent({ setup(props, { attrs, listeners }) { return () => ( <div> <h3>Child Component</h3> {/* 通过 spread operator 将 $attrs 应用到子组件的根元素上 */} <div {...attrs}> <p>Message from parent: {props.message}</p> {/* 通过 $listeners 添加事件监听器 */} <button onClick={listeners.onClick}>Click me</button> </div> </div> ); } });
|
$refs:
父组件可以通过 ref 属性给子组件赋予一个引用,然后通过 $refs 来直接访问子组件的属性和方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| export default defineComponent({ setup() { const childRef = ref(null);
const callChildMethod = () => { if (childRef.value) { childRef.value.childMethod(); } };
return { childRef, callChildMethod }; }, render() { return ( <div> <h2>Parent Component</h2> {/* 通过 ref 绑定子组件 */} <ChildComponent ref={this.childRef} /> <button onClick={this.callChildMethod}>Call Child Method</button> </div> ); } });
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const ChildComponent = defineComponent({ setup() { const childMethod = () => { console.log('Method called in ChildComponent'); };
return { childMethod }; }, render() { return ( <div> <h3>Child Component</h3> </div> ); } });
|
二、Vue2.x通信方式
Props / Events(属性 / 事件)
父组件通过 props 向子组件传递数据,子组件通过触发事件向父组件发送消息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| <template> <div> <h2>Parent Component</h2> <ChildComponent :message="parentMessage" @notify="handleNotify" /> <p>Message from Child: {{ childMessage }}</p> </div> </template>
<script> import ChildComponent from './ChildComponent.vue';
export default { components: { ChildComponent }, data() { return { parentMessage: 'Hello from parent', childMessage: '' }; }, methods: { handleNotify(message) { this.childMessage = message; } } } </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <template> <div> <h3>Child Component</h3> <button @click="notifyParent">Notify Parent</button> </div> </template>
<script> export default { methods: { notifyParent() { this.$emit('notify', 'Hello from child'); } } } </script>
|
Vuex
Vuex 是 Vue.js 的状态管理库,可以在应用中集中管理状态,不同组件可以通过 Vuex 中的 state、mutations、actions 等进行通信。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| // store.js import { createStore } from 'vuex';
// 创建一个 Vuex store const store = createStore({ state() { return { count: 0 }; }, mutations: { // 定义一个 mutation 来更新 count 的值 increment(state) { state.count++; } }, actions: { // 定义一个 action 来触发 increment mutation incrementCount(context) { context.commit('increment'); } }, getters: { // 定义一个 getter 来获取 count 的值 getCount(state) { return state.count; } } });
export default store;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <template> <div> <h2>Count: {{ count }}</h2> <button @click="incrementCount">Increment</button> </div> </template>
<script> export default { computed: { count() { return this.$store.getters.getCount; } }, methods: { incrementCount() { this.$store.dispatch('incrementCount'); } } } </script>
|
Provide / Inject
父组件通过 provide 提供数据,子组件通过 inject 注入数据,实现祖先组件向后代组件的数据传递。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <template> <div> <h2>Parent Component</h2> <provide :sharedData="sharedData"> <ChildComponent /> </provide> </div> </template>
<script> import ChildComponent from './ChildComponent.vue';
export default { components: { ChildComponent }, data() { return { sharedData: 'Data shared from parent' }; } } </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <template> <div> <h3>Child Component</h3> <p>Shared Data: {{ sharedData }}</p> </div> </template>
<script> export default { inject: ['sharedData'] } </script>
|
Event Bus(事件总线)
创建一个新的 Vue 实例作为事件总线,用于在组件之间进行事件的发布和订阅。
1 2 3 4 5 6 7 8
| import { createApp } from 'vue';
const eventBus = createApp({}); const bus = eventBus.config.globalProperties;
export default bus;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <template> <div> <h2>Component A</h2> <button @click="sendMessage">Send Message to B</button> </div> </template>
<script> import bus from './EventBus.js';
export default { methods: { sendMessage() { bus.$emit('message', 'Hello from Component A'); } } } </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <template> <div> <h2>Component B</h2> <p>Received Message: {{ receivedMessage }}</p> </div> </template>
<script> import bus from './EventBus.js';
export default { data() { return { receivedMessage: '' }; }, created() { bus.$on('message', (message) => { this.receivedMessage = message; }); } } </script>
|
$attrs / $listeners
在 Vue 2.6 中引入的特性,用于在组件中传递父组件的属性和监听器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <template> <div> <h2>Parent Component</h2> <ChildComponent title="Title from parent" @click="handleClick" /> </div> </template>
<script> import ChildComponent from './ChildComponent.vue';
export default { components: { ChildComponent }, methods: { handleClick() { alert('Parent click event triggered'); } } } </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <template> <div> <h2>Child Component</h2> <p v-for="(value, key) in $attrs" :key="key">{{ key }}: {{ value }}</p> <button @click="$listeners.click">Trigger Parent Click Event</button> </div> </template>
<script> export default { inheritAttrs: false } </script>
|
$parent / $children
可以通过 $parent 直接访问父组件实例,通过 $children 访问子组件实例,但不推荐使用这种方式进行通信,因为它使得组件之间的依赖关系变得不明确,不利于维护和调试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <template> <div> <h2>Parent Component</h2> <p>Parent Component Message: {{ message }}</p> <ChildComponent /> </div> </template>
<script> import ChildComponent from './ChildComponent.vue';
export default { components: { ChildComponent }, data() { return { message: 'Hello from parent!' }; } } </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <template> <div> <h3>Child Component</h3> <p>Child Component Message: {{ message }}</p> </div> </template>
<script> export default { created() { console.log('Parent component message:', this.$parent.message); }, data() { return { message: 'Hello from child!' }; } } </script>
|