Vue组件通信全篇
说到Vue3的组件通信,还得从Vue2的组件通信说起, 小伙伴们查漏补缺哈
首先我们来看下组件的几种用法循序渐进:
Vue2的用法总结下帮助小伙伴们加深下记忆,如果还没不是很熟悉基础的小伙伴可以去看下官网。
-
父子组件通信
- props 父到子
- $emit 子到父
- ref、$refs 子到父
- $parent 子到父
- $children 父到子 (Vue3此方法作废)
-
多层级父子组件通信
-
provide、inject 父到子 或者 子到父
// provide 声明 provide() { return { title: '我是app', ComApp: this, } } // inject 使用 // 使用的时候可以拿到父组件的this也就是ComApp进行传递数据 inject: ['title', 'ComApp'],
-
$attrs 利用属性的传递数据配合inheritAttrs使用
-
$listeners (Vue3此方法作废监听功能是 attrs的一部分)
-
-
非关系组件通信(EventBus也称为事件总线)
Vue2的EventBus:
// eventBus.js const eventBus = new Vue() export default eventBus
// ChildComponent.vue import eventBus from './eventBus' export default { mounted() { // 添加 eventBus 监听器 eventBus.$on('custom-event', () => { console.log('Custom event triggered!') }) }, beforeDestroy() { // 移除 eventBus 监听器 eventBus.$off('custom-event') } }
// ParentComponent.vue import eventBus from './eventBus' export default { methods: { callGlobalCustomEvent() { eventBus.$emit('custom-event') // 当 ChildComponent 被挂载,控制台中将显示一条消息 } } }
Vue3中完全移除了
$on
、$off
和$once
方法。$emit
仍然包含于现有的 API 中,因为它用于触发由父组件声明式添加的事件处理函数。Vue3的EventBus:
// main.js import mitt from 'mitt' let app = createApp(App) app.config.globalProperties.$myhub = new mitt()
// ParentComponent.vue export default { // ..... methods: { getattrs() { this.$myhub.emit('myhub', '我是小白龙') } }
// ChildComponent.vue export default { // ..... mounted() { this.$myhub.on('myhub', (data) => { console.log(data) // 我是小白龙 }) }, }
总结:
在绝大多数情况下,不鼓励使用全局的 event bus 在组件之间进行通信。虽然在短期内往往是最简单的解决方案,但从长期来看,它维护起来总是令人头疼。根据具体情况来看,有多种 event bus 的替代方案:
- Prop 和事件应该是父子组件之间沟通的首选。兄弟节点可以通过它们的父节点通信。
- Provide 和 inject 允许一个组件与它的插槽内容进行通信。这对于总是一起使用的紧密耦合的组件非常有用。
provide
/inject
也能够用于组件之间的远距离通信。它可以帮助避免“prop 逐级透传”,即 prop 需要通过许多层级的组件传递下去,但这些组件本身可能并不需要那些 prop。- Prop 逐级透传也可以通过重构以使用插槽来避免。如果一个中间组件不需要某些 prop,那么表明它可能存在关注点分离的问题。在该类组件中使用 slot 可以允许父节点直接为它创建内容,因此 prop 可以被直接传递而不需要中间组件的参与。
- 全局状态管理,比如 Vuex。
-
Vuex
<template> //store.state.count取值 <div>{{ $store.state.count }}</div> <button @click="myCommit ">点击</button> </template> <script setup lang="ts"> import { useStore } from 'vuex'; const store = useStore(); //commit提交 const myCommit = () =>{ store.commit("commitValue") } </script>
组件通信最好用的还是Vuex, 但是多多少少还是存在一些问题。
Vuex唯有一点不好就是TypeScript的支持问题:
- 不用TypeScript时候在调用state、getters、mutation、dispatch的时候没有智能提示,那么多文件可找吧!!!!!
- 用了TypeScript以后发现解决这个问题,但是发现Vuex对TypeScript的Types支持并没有想想中的那么香(这里说的是Vuex4)。
虽然Vuex4提供了useStore函数, 但是并没有对store做的像redux那样丝滑的推导体验。
有人的想法非常棒,把Vuex的store初始化的时候重新扩展了下,这样就能很好的获得TypeScript对Vuex的各种支持。
其实Vuex团队也意识到这个问题, 在Vuex5的时候改纯函数写法了,这样就能很好的支持TypeScript。
Vuex5已经到RC阶段, 现阶段我们可以尝试使用 Pinia
5.Pinia(官网)
Pinia 是 Vue.js 的轻量级状态管理库,最近很受欢迎。
类似 Vuex, 是 Vue 的另一种状态管理方案,Pinia 支持 Vue2 和 Vue3以及更好的支持TypeScript。
使用:
-
创建Pinia传递给app
// 使用pinia import { createPinia } from 'pinia'; const app = createApp(App) // 挂到app上 app.use(createPinia()) app.mount('#app')
-
创建Store
import { defineStore } from "pinia"; // defineStore 调用后返回一个函数,调用该函数获得 Store 实体 export const useStore = defineStore({ // id: 必须的,在所有 Store 中唯一 id: "myGlobalState", // state: 返回对象的函数 state: () => ({ count: 1 }), });
-
组件中使用Stroe
<template> <div> {{store.count}} </div> </template> <script> import { defineComponent } from 'vue' import { useStore } from './store/store.js' export default defineComponent({ name: 'App', setup() { // 调用函数 获得Store const store = useStore() return { store, } }, }) </script>
篇幅原因在这就不啰嗦了, 感兴趣的小伙伴可以去官网上好好看看api
-
Vuex 和 Pinia 的优缺点
Vuex的优点
- 支持调试功能,如时间旅行和编辑
- 适用于大型、高复杂度的Vue.js项目
Vuex的缺点
- 从 Vue 3 开始,getter 的结果不会像计算属性那样缓存
- Vuex 4有一些与类型安全相关的问题
Pinia的优点
- 完整的 TypeScript 支持:与在 Vuex 中添加 TypeScript 相比,添加 TypeScript 更容易
- 极其轻巧(体积约 1KB)
- store 的 action 被调度为常规的函数调用,而不是使用
dispatch
方法或MapAction
辅助函数,这在 Vuex 中很常见 - 支持多个Store
- 支持 Vue devtools、SSR 和 webpack 代码拆分
Pinia的缺点
- 不支持时间旅行和编辑等调试功能
-
何时使用Pinia,何时使用Vuex
-
Pinea是轻量级的,体积很小,它适合于中小型应用。它也适用于低复杂度的Vue.js项目,因为一些调试功能,如时间旅行和编辑仍然不被支持。
-
Vuex 用于中小型 Vue.js 项目是过度的,因为它重量级的,对性能降低有很大影响。因此,Vuex 适用于大规模、高复杂度的 Vue.js 项目。
-
欢迎大佬们留言指正,坚持每天进步一点点。