vue十四种组件通信方式

230 阅读4分钟

在 Vue 中,主要有以下几种通信方式: 一、父子组件通信

  1. 父传子

    • 通过 props 向子组件传递数据。
    • 父组件在使用子组件时,通过属性绑定的方式将数据传递给子组件的 props。
    • 例如:

父组件中

<child-component :dataFromParent="parentData"></child-component>

子组件

  • 在子组件中通过props选项接收数据:
 export default {
   props: ['dataFromParent'],
 };
  1. 子传父

    • 通过$emit触发自定义事件,并向父组件传递数据。
    • 子组件在需要向父组件传递数据时,使用this.$emit('customEvent', data)的方式触发一个自定义事件,并将数据作为参数传递。
    • 父组件在使用子组件时,通过v-on监听这个自定义事件,并在事件处理函数中接收数据。
    • 例如:
<child-component @customEvent="handleChildData"></child-component>
<script>
export default{
    methods: {
       handleChildData(data) {
         // 处理子组件传递过来的数据
       },
     },
 }
</script>

二、兄弟组件通信

  1. 通过共同的父组件作为媒介

    • 兄弟组件 A 向父组件传递数据,父组件接收数据后,再将数据传递给兄弟组件 B。
    • 步骤与父子组件通信类似,只是涉及到两个子组件与父组件之间的交互。
  2. 使用事件总线(Event Bus)

    • 创建一个空的 Vue 实例作为事件总线。
    • 在需要通信的组件中引入事件总线,并通过$emit触发事件和$on监听事件来实现通信。
    • 例如:

     // 创建事件总线
     const eventBus = new Vue();

     // 组件 A
     export default {
       methods: {
         sendDataToBrother() {
           eventBus.$emit('customEvent', data);
         },
       },
     };

     // 组件 B
     export default {
       created() {
         eventBus.$on('customEvent', (data) => {
           // 处理接收到的数据
         });
       },
     };

三、跨级组件通信

  1. 通过 provide 和 inject

    • 祖先组件通过provide选项提供数据。
    • 后代组件通过inject选项注入数据。
    • 例如:
     // 祖先组件
     export default {
       provide: {
         dataForDescendants: 'someData',
       },
     };

     // 后代组件
     export default {
       inject: ['dataForDescendants'],
     };

Vue 提供了多种灵活的通信方式,可以根据具体的应用场景选择合适的方法来实现组件之间的数据交互。 四、Vuex 状态管理

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态。

  1. 核心概念:

    • State:存储应用的状态数据。
    • Mutations:用于更改状态的方法,必须是同步函数。
    • Actions:可以包含异步操作,通过提交 mutations 来更改状态。
    • Getters:可以对 state 进行计算,类似于计算属性。
  2. 使用步骤:

    • 安装 Vuex:npm install vuex
    • 创建一个 store 对象:
 import Vue from 'vue';
     import Vuex from 'vuex';

     Vue.use(Vuex);

     const store = new Vuex.Store({
       state: {
         count: 0,
       },
       mutations: {
         increment(state) {
           state.count++;
         },
       },
       actions: {
         incrementAsync({ commit }) {
           setTimeout(() => {
             commit('increment');
           }, 1000);
         },
       },
       getters: {
         doubleCount(state) {
           return state.count * 2;
         },
       },
     });

     export default store;
  • 在 Vue 组件中使用:
 <template>
       <div>
         <p>Count: {{ count }}</p>
         <button @click="increment">Increment</button>
         <button @click="incrementAsync">Increment Async</button>
       </div>
</template>

 <script>
 import { mapState, mapMutations, mapActions } from 'vuex';

 export default {
   computed: {
    ...mapState(['count']),
   },
   methods: {
    ...mapMutations(['increment']),
    ...mapActions(['incrementAsync']),
   },
 };
 </script>

五、使用 localStorage 或 sessionStorage

可以将数据存储在浏览器的本地存储或会话存储中,实现组件之间的数据共享。但这种方式不适合大量数据或频繁更新的数据,并且可能存在数据同步问题。

  1. 存储数据:

    • localStorage.setItem('key', value):将数据存储到本地存储中。
    • sessionStorage.setItem('key', value):将数据存储到会话存储中。
  2. 获取数据:

    • localStorage.getItem('key'):从本地存储中获取数据。
    • sessionStorage.getItem('key'):从会话存储中获取数据。
  3. 例如:

    • 组件 A 将数据存储到本地存储:
 localStorage.setItem('sharedData', 'someValue');
  • 组件 B 从本地存储中获取数据:
     const data = localStorage.getItem('sharedData');

六、使用插件进行通信

可以开发自定义插件来实现特定的通信需求。插件可以在 Vue 的全局范围内提供功能,使得各个组件可以方便地使用插件提供的方法进行通信。

  1. 创建插件:

    • 例如:
     const myPlugin = {
       install(Vue) {
         Vue.prototype.$myCommunicationMethod = function(data) {
           // 实现通信的逻辑
         };
       },
     };
  1. 使用插件:

    • 在 Vue 应用中安装插件:
     Vue.use(myPlugin);
  • 在组件中使用插件提供的方法进行通信:
   this.$myCommunicationMethod('someData');

七、路由参数传递

在 Vue Router 中,可以通过路由参数在不同页面(组件)之间传递数据。

  1. 定义带有参数的路由:
   const routes = [
     {
       path: '/user/:id',
       name: 'User',
       component: UserComponent,
     },
   ];

传递参数:

  • 通过编程式导航传递参数
  this.$router.push({ name: 'User', params: { id: 123 } });
  • 或者通过链接传递参数:<router-link :to="{ name: 'User', params: { id: 123 } }">User Page</router-link>

  1. 在目标组件中接收参数:
  export default {
     created() {
       const userId = this.$route.params.id;
       // 使用参数进行操作
     },
   };

八、attrsattrs 和 listeners

  1. attrs:包含了父组件传递给当前组件的非属性。可以通过attrs"` 将这些属性继续向下传递给子组件。

    例如:

 <!-- 父组件 -->
   <parent-component some-attr="value">
     <child-component></child-component>
   </parent-component>

   <!-- 子组件 -->
   <template>
     <div>
       <grandchild-component v-bind="$attrs"></grandchild-component>
     </div>
   </template>
  1. listeners:包含了父组件作用在当前组件上的监听器。可以通过listeners"` 将这些监听器继续向下传递给子组件。

    例如:

 <!-- 父组件 -->
   <parent-component @custom-event="parentMethod">
     <child-component></child-component>
   </parent-component>

   <!-- 子组件 -->
   <template>
     <div>
       <grandchild-component v-on="$listeners"></grandchild-component>
     </div>
   </template>

九、Broadcast 和 Dispatch

在一些复杂的组件结构中,可以使用自定义的广播(Broadcast)和派发(Dispatch)方法来实现跨层级的通信。

  1. 创建 Broadcast 和 Dispatch 方法:
 function broadcast(componentName, eventName, params) {
     this.$children.forEach(child => {
       if (child.$options.name === componentName) {
         child.$emit(eventName, params);
       } else {
         broadcast.apply(child, [componentName, eventName, params]);
       }
     });
   }

   function dispatch(componentName, eventName, params) {
     let parent = this.$parent;
     while (parent) {
       if (parent.$options.name === componentName) {
         parent.$emit(eventName, params);
         break;
       }
       parent = parent.$parent;
     }
   }

   Vue.prototype.$broadcast = broadcast;
   Vue.prototype.$dispatch = dispatch;
  1. 使用方法:

    • 在组件中派发事件:this.$dispatch('componentName', 'eventName', params)
    • 在组件中广播事件:this.$broadcast('componentName', 'eventName', params)

十、使用第三方库进行通信

例如使用 RxJS 等响应式编程库,可以实现复杂的数据流管理和组件通信。

  1. 安装 RxJS:npm install rxjs
  2. 创建主题(Subject)进行通信:
 import { Subject } from 'rxjs';

   const subject = new Subject();

   // 发送数据
   subject.next('data');

   // 在组件中订阅主题
   subject.subscribe(data => {
     // 处理接收到的数据
   });

十一、Vue 响应式对象共享

可以创建一个响应式对象,并将其注入到多个组件中进行共享和通信。

  1. 创建响应式对象:
 import Vue from 'vue';

   const sharedData = Vue.observable({
     message: 'Initial message',
   });
  1. 在组件中使用:
 <template>
     <div>
       <p>{{ sharedData.message }}</p>
       <button @click="updateMessage">Update Message</button>
     </div>
   </template>

   <script>
   import { sharedData } from './path/to/sharedData';

   export default {
     data() {
       return {
         // 这里不需要定义 message,直接使用共享对象
       };
     },
     methods: {
       updateMessage() {
         sharedData.message = 'Updated message';
       },
     },
   };
   </script>

十二、服务(Service)模式通信

创建一个服务对象,用于管理共享的数据和逻辑,各个组件可以通过引入这个服务对象进行通信。

  1. 创建服务:
 class DataService {
     constructor() {
       this.data = 'Initial data';
     }

     updateData(newData) {
       this.data = newData;
     }
   }

   export default new DataService();
  1. 在组件中使用:
<template>
     <div>
       <p>{{ dataService.data }}</p>
       <button @click="updateData">Update Data</button>
     </div>
   </template>

   <script>
   import DataService from './path/to/DataService';

   export default {
     data() {
       return {
         dataService: DataService,
       };
     },
     methods: {
       updateData() {
         this.dataService.updateData('Updated data');
       },
     },
   };
   </script>

十三、使用 Vue 的全局事件总线(Event Bus)的高级用法

  1. 可以创建多个事件总线,用于不同类型的通信场景,避免事件冲突。
 const eventBus1 = new Vue();
   const eventBus2 = new Vue();

   // 在组件 A 中使用 eventBus1
   eventBus1.$emit('event1', data);

   // 在组件 B 中监听 eventBus1 的事件
   eventBus1.$on('event1', (data) => {
     // 处理事件
   });

   // 在组件 C 中使用 eventBus2
   eventBus2.$emit('event2', data);

   // 在组件 D 中监听 eventBus2 的事件
   eventBus2.$on('event2', (data) => {
     // 处理事件
   });
  1. 结合命名空间来管理事件,使事件更加清晰和易于维护。
const eventBus = new Vue();

 const namespace1 = 'namespace1';
 const namespace2 = 'namespace2';

 // 触发命名空间下的事件
 eventBus.$emit(`${namespace1}:event`, data);
 eventBus.$emit(`${namespace2}:event`, data);

 // 监听命名空间下的事件
 eventBus.$on(`${namespace1}:event`, (data) => {
   // 处理 namespace1 下的事件
 });
 eventBus.$on(`${namespace2}:event`, (data) => {
   // 处理 namespace2 下的事件
 });

这些通信方式可以根据具体的项目需求和架构选择使用,以实现高效的 Vue 组件间通信。

十四、使用 Vue 的全局事件总线(Event Bus)的高级用法 一、在父组件中使用v-model

在父组件中,可以像使用原生表单元素一样使用v-model指令在一个自定义组件上,实现数据的双向绑定。

例如:

<template>
  <div>
    <my-custom-component v-model="parentData"></my-custom-component>
    <p>Parent Data: {{ parentData }}</p>
  </div>
</template>

<script>
import MyCustomComponent from './MyCustomComponent.vue';

export default {
  components: {
    MyCustomComponent,
  },
  data() {
    return {
      parentData: 'Initial value',
    };
  },
};
</script>

二、在子组件中实现v-model功能

在子组件中,要实现与父组件中v-model的双向绑定效果,需要以下两个步骤:

  1. 接收一个名为value的 prop,这是父组件传递过来的数据。
  2. 在子组件内部触发一个自定义事件并将新的值传递出去,通常这个事件名为input

例如:

<template>
  <div>
    <input type="text" :value="inputValue" @input="onInputChange">
  </div>
</template>

<script>
export default {
  props: ['value'],
  computed: {
    inputValue: {
      get() {
        return this.value;
      },
      set(newValue) {
        this.$emit('input', newValue);
      },
    },
  },
  methods: {
    onInputChange(event) {
      this.$emit('input', event.target.value);
    },
  },
};
</script>