Vue组件通信最全篇 | 8月更文挑战

1,263 阅读4分钟

Vue组件通信全篇

说到Vue3的组件通信,还得从Vue2的组件通信说起, 小伙伴们查漏补缺哈

拍脑门.gif

首先我们来看下组件的几种用法循序渐进:

Vue2的用法总结下帮助小伙伴们加深下记忆,如果还没不是很熟悉基础的小伙伴可以去看下官网

  1. 父子组件通信

    • props 父到子
    • $emit 子到父
    • ref、$refs 子到父
    • $parent 子到父
    • $children 父到子 (Vue3此方法作废)
  2. 多层级父子组件通信

    • provide、inject 父到子 或者 子到父

      // provide 声明
      provide() {
        return {
          title: '我是app',
          ComApp: this, 
        }
      }
      // inject 使用
      // 使用的时候可以拿到父组件的this也就是ComApp进行传递数据
       inject: ['title', 'ComApp'], 
      
    • $attrs 利用属性的传递数据配合inheritAttrs使用

    • $listeners (Vue3此方法作废监听功能是 attrs的一部分)

  3. 非关系组件通信(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
  4. 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。

nice.gif

Vuex5已经到RC阶段, 现阶段我们可以尝试使用 Pinia

5.Pinia(官网)

Pinia 是 Vue.js 的轻量级状态管理库,最近很受欢迎。

类似 Vuex, 是 Vue 的另一种状态管理方案,Pinia 支持 Vue2 和 Vue3以及更好的支持TypeScript。

使用:

  1. 创建Pinia传递给app

    // 使用pinia
    import { createPinia } from 'pinia';
    const app = createApp(App)
    // 挂到app上
    app.use(createPinia())
    app.mount('#app')
    
  2. 创建Store

    import { defineStore } from "pinia";
    
    // defineStore 调用后返回一个函数,调用该函数获得 Store 实体
    export const useStore = defineStore({
      // id: 必须的,在所有 Store 中唯一
      id: "myGlobalState",
      // state: 返回对象的函数
      state: () => ({
        count: 1
      }),
    });
    
  3. 组件中使用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

  4. 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的缺点

    • 不支持时间旅行和编辑等调试功能
  5. 何时使用Pinia,何时使用Vuex

    • Pinea是轻量级的,体积很小,它适合于中小型应用。它也适用于低复杂度的Vue.js项目,因为一些调试功能,如时间旅行和编辑仍然不被支持。

    • Vuex 用于中小型 Vue.js 项目是过度的,因为它重量级的,对性能降低有很大影响。因此,Vuex 适用于大规模、高复杂度的 Vue.js 项目。

加油.jpeg

欢迎大佬们留言指正,坚持每天进步一点点。

公众号.jpg