Vue3如何优雅的跨组件通信
2023-12-14 19:35:12
开发中经常会遇到跨组件通信的场景。props 逐层传递的方法实在是太不优雅了,所以今天总结下可以更加简单的跨组件通信的一些方法。
依赖注入
<!-- App.vue -->
<script setup lang="ts">
import { ref, provide } from "vue";
import Child from "./components/Child.vue";
const count = ref(0)
const updateCount = () => count.value ++
provide("count", {count, updateCount})
</script>
<template>
<h4>XXXX</h4>
<div>{{ count }}</div>
<button @click="updateCount">change</button>
<Child />
</template>
<!-- Child.vue -->
<template>
<Other />
</template>
<script setup lang='ts'>
import Other from "./other.vue"
</script>
<!-- other.vue -->
<template>
<div>{{ count }}</div>
<button @click="updateCount">other change</button>
</template>
<script setup lang='ts'>
import { inject } from "vue"
const {count, updateCount} = inject("count")
</script>
在 setup
组件中,使用 inject
跨组件通信是最佳的方案。所以该模式下,是没有提供event bus
事件总线。
但是在 option api
模式下,还需要额外的注册,显的有点麻烦。
<script lang='ts'>
export default {
emits: ["some-name"]
}
</script>
属性透传
<!-- App.vue -->
<script setup lang="ts">
import { ref, provide } from "vue";
import Attr from "./components/Attr.vue";
const count = ref(0)
const updateCount = () => count.value ++
provide("count", {count, updateCount})
</script>
<template>
<h4>XXXX</h4>
<div>{{ count }}</div>
<button @click="updateCount">change</button>
<Attr :count="count" :updateCount="updateCount" />
</template>
<!-- Attr.vue -->
<template>
<div>attr component</div>
<Child v-bind="$attrs" />
</template>
<script setup lang='ts'>
import Child from './Child.vue';
</script>
属性透传这种方式类似于react中手动透传属性。感觉有点暴力,但是又特别方便快捷。
function App (props) {
return <Other {...props} />
}
Vue中默认透传的属性有 style、class、key。如果子组件也存在class、style,则会自动合并class、style。
如果你的子组件是根组件时,可以省略 v-bind="$attrs"
。
<template>
<Child />
</template>
状态库
状态管理库我们以Pinia为例。
<!-- App.vue -->
<script setup lang="ts">
import Other from "./components/Other.vue";
import { useCounterStore } from "./store/index"
const state = useCounterStore()
</script>
<template>
<h4>XXXX</h4>
<div>{{ count }}</div>
<button @click="updateCount">change</button>
<Other />
</template>
import { defineStore } from "pinia"
import { ref } from "vue"
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
function updateCount() {
count.value++
}
return { count, updateCount }
})
<!-- Other.vue -->
<template>
<div>pinia store</div>
<div>{{ state.count }}</div>
<button @click="state.updateCount">other change</button>
</template>
<script setup lang='ts'>
import { useCounterStore } from '../store';
const state = useCounterStore()
</script>
状态管理库最大的缺点是,没法使用解构语法。因为这会导致失去响应式的能力。
事件总线
事件总线(event bus
)比较特殊,因为在组合式API里不支持该方式,所以下面的例子适合 Option API 组件。
<!-- App.vue -->
<script setup lang="ts">
import { ref } from "vue";
import Other from "./components/Other.vue";
const count = ref(0)
const updateCount = () => count.value ++
</script>
<template>
<h4>XXXX</h4>
<div>{{ count }}</div>
<button @click="updateCount">change</button>
<Other @updateCount="updateCount()" />
</template>
<!-- Other.vue -->
<template>
<div>eventBus store</div>
<button @click="$emit('updateCount')">other change</button>
</template>
<script lang='ts'>
export default {
emits: ["updateCount"]
}
</script>
事件总线更适合传递事件。
自定义事件
但是有时候,你可能非常想使用事件总线的方式在 setup
组件中传递事件,这时候我们可以使用自定义的事件的方式实现这种功能。
class EventBus {
constructor() {
this.events = {};
}
// 订阅事件
on(eventName, callback) {
if (!this.events[eventName]) {
this.events[eventName] = [];
}
this.events[eventName].push(callback);
}
// 发布事件
emit(eventName, eventData) {
const eventCallbacks = this.events[eventName];
if (eventCallbacks) {
eventCallbacks.forEach(callback => {
callback(eventData);
});
}
}
// 取消订阅事件
off(eventName, callback) {
const eventCallbacks = this.events[eventName];
if (eventCallbacks) {
this.events[eventName] = eventCallbacks.filter(cb => cb !== callback);
}
}
}
export const eventBus = new EventBus()
<!-- App.vue -->
<script setup lang="ts">
import { ref } from "vue";
import Other from "./components/Other.vue";
import { eventBus } from "./store/eventBus";
const count = ref(0)
const updateCount = () => count.value ++
eventBus.on("updateCount", updateCount)
</script>
<template>
<h4>XXXX</h4>
<div>{{ count }}</div>
<button @click="updateCount">change</button>
<Other @updateCount="updateCount()" />
</template>
<!-- Other.vue -->
<template>
<div>eventBus</div>
<button @click="eventBus.emit('updateCount', null)">other change</button>
</template>
<script setup lang='ts'>
import { eventBus } from "../store/eventBus";
</script>
当然,我们这里不止可以使用 event bus,发布订阅模式也很适合。可以参考我以前的设计模式的文章实现这个功能。
总结
每种方式都有自己的优点和缺点,根据使用场景选择最合适的才能算是最优的方案。
文章来源:https://blog.csdn.net/weixin_45788691/article/details/134873099
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!