【vue】vue通信方式

vue 通信方式

相关文章

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
// ParentComponent.jsx
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
// ChildComponent.jsx
import { defineComponent, ref } from 'vue';

export default defineComponent({
props: {
// 声明一个名为 message 的 prop
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
// ParentComponent.jsx
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
// ChildComponent.jsx
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
// store.js
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
// App.jsx
import { defineComponent } from 'vue';
import { createPinia } from 'pinia';
import { useCounterStore } from './store';

// 创建 Pinia 实例
const pinia = createPinia();

export default defineComponent({
setup() {
// 注册 Pinia 实例
pinia.useStore(useCounterStore);

// 从 Pinia 中获取计数器存储
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
// EventBus.jsx
import { createApp, ref } from 'vue';

// 创建一个新的 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
// ComponentA.jsx
import { defineComponent } from 'vue';
import { onEvent, emitEvent } from './EventBus.jsx';

export default defineComponent({
setup() {
// 监听来自 EventBus 的 customEvent 事件
onEvent('customEvent', (data) => {
console.log('Received data in ComponentA:', data);
});

// 触发一个 customEvent 事件
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
// ComponentB.jsx
import { defineComponent } from 'vue';
import { onEvent } from './EventBus.jsx';

export default defineComponent({
setup() {
// 监听来自 EventBus 的 customEvent 事件
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
// ParentComponent.jsx
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
// ChildComponent.jsx
import { defineComponent } from 'vue';

export default defineComponent({
setup(props, { attrs, listeners }) {
// 在子组件中使用 $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
// ParentComponent.jsx
export default defineComponent({
setup() {
// 创建一个 ref,用于引用子组件实例
const childRef = ref(null);

// 创建一个方法,在父组件中通过 $refs 调用子组件的方法
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
// ChildComponent.jsx
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
<!-- ParentComponent.vue -->
<template>
<div>
<h2>Parent Component</h2>
<!-- 使用 props 向子组件传递数据 -->
<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
<!-- ChildComponent.vue -->
<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
<!-- App.vue -->
<template>
<div>
<h2>Count: {{ count }}</h2>
<!-- 调用 incrementCount action 来增加 count -->
<button @click="incrementCount">Increment</button>
</div>
</template>

<script>
export default {
computed: {
// 从 Vuex store 中获取 count 的值
count() {
return this.$store.getters.getCount;
}
},
methods: {
// 调用 Vuex store 中的 incrementCount action
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
<!-- ParentComponent.vue -->
<template>
<div>
<h2>Parent Component</h2>
<!-- 使用 provide 来提供数据 -->
<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
<!-- ChildComponent.vue -->
<template>
<div>
<h3>Child Component</h3>
<!-- 使用 inject 来注入父组件提供的数据 -->
<p>Shared Data: {{ sharedData }}</p>
</div>
</template>

<script>
export default {
inject: ['sharedData']
}
</script>

Event Bus(事件总线)

创建一个新的 Vue 实例作为事件总线,用于在组件之间进行事件的发布和订阅。

1
2
3
4
5
6
7
8
// EventBus.js
import { createApp } from 'vue';

// 创建一个新的 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
<!-- ComponentA.vue -->
<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
<!-- ComponentB.vue -->
<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() {
// 监听事件总线上的 'message' 事件
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
<!-- ParentComponent.vue -->
<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
<!-- ChildComponent.vue -->
<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 // 禁用默认的 attribute 继承行为
}
</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
<!-- ParentComponent.vue -->
<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
<!-- ChildComponent.vue -->
<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>

喜欢这篇文章?打赏一下支持一下作者吧!
【vue】vue通信方式
https://www.cccccl.com/20211024/vue/vue 通信方式/
作者
Jeffrey
发布于
2021年10月24日
许可协议