【vuejs】常用的组件之间数据通信方式总结

vue.js组件实例的作用域是相互独立的,不同组件之间的数据不能相互访问,组件有父级组件、子级组件、兄弟组件、如何选择组件之间的通信方式?今天就来总结一下常用的几种组件传值方式

1. 父子组件通信

1.1 Props

Props 是 Vue.js 中用于父子组件间数据传递的一种方式。父组件通过 props 向子组件传递数据,子组件通过声明 props 属性来接收这些数据。

  • 单向数据流:Props 确保了数据流的单向性,即数据只能从父组件流向子组件,这样做提高了组件间的解耦性。
  • 动态 Props:父组件可以使用 v-bind 动态地将数据对象传递给子组件,v-bind 可以是任何类型的数据,包括对象和数组。
  • 示例:
    <template>
      <child-component v-bind:dynamic-prop="parentData"></child-component>
    </template>
    <script>
    export default {
      data() {
        return {
          parentData: {
            message: 'Hello from parent!'
          }
        };
      }
    };
    </script>
    

1.2 e m i t / emit/ emit/on

当需要从子组件向父组件发送数据时,可以使用 $emit 来触发事件,并通过 $on 方法在父组件中监听这些事件。

  • 自定义事件:子组件通过 this.$emit 触发一个事件,并可以附带数据,如 this.$emit('custom-event', eventData)
  • 监听事件:父组件使用 v-on@ 语法来监听子组件触发的事件,如 <child-component @custom-event="handleEvent">
  • 事件对象:在事件处理函数中,可以访问到事件对象,包含有关事件的信息,如时间戳和事件类型。
  • 示例:
    <template>
      <child-component @trigger="handleTrigger"></child-component>
    </template>
    <script>
    export default {
      methods: {
        handleTrigger(data) {
          console.log('Event triggered with data:', data);
        }
      }
    };
    </script>
    

2. 事件总线通信

2.1 创建事件总线

事件总线(Event Bus)是一种在 Vue.js 应用程序中实现跨组件通信的方法。它通过创建一个新的 Vue 实例作为中央事件总线,使得不同的组件能够相互通信,无论它们是否在组件树中直接相关。

  • 实现方式:首先,创建一个事件总线的实例文件,例如 event-bus.js,并导出一个新的 Vue 实例。
    import Vue from 'vue';
    const EventBus = new Vue();
    export default EventBus;
    
  • 全局挂载:在主入口文件,如 main.js 中,将事件总线实例挂载到 Vue 的原型上,以便在任何组件中都能够访问。
    import Vue from 'vue';
    import App from './App.vue';
    import EventBus from './event-bus.js';
    
    Vue.prototype.$eventBus = EventBus;
    new Vue({
      render: h => h(App),
    }).$mount('#app');
    

2.2 事件触发与监听

使用事件总线进行通信时,组件可以触发(emit)事件和监听(on)事件。

  • 触发事件:任何组件都可以通过 this.$eventBus.$emit 方法触发事件,允许其他组件监听这些事件。
    this.$eventBus.$emit('custom-event', 'Some data');
    
  • 监听事件:组件可以通过 this.$eventBus.$on 方法监听事件,并定义一个回调函数来响应事件。
    this.$eventBus.$on('custom-event', (data) => {
      console.log('Event received:', data);
    });
    
  • 移除事件监听:如果需要,可以使用 $off 方法从事件总线移除监听器。
    this.$eventBus.$off('custom-event', listenerFunction);
    
  • 一次性监听:有时我们只想响应一次事件,这时可以使用 $once 方法,事件触发后监听器将自动被移除。
    this.$eventBus.$once('custom-event', (data) => {
      console.log('This will only trigger once:', data);
    });
    

事件总线提供了一种非常灵活的组件间通信方式,特别适合于非父子组件间的通信,或者当组件间的层级关系较远时。然而,使用事件总线时应谨慎,以避免组件间的过度耦合,确保代码的可维护性。

3. Vuex状态管理

3.1 State

Vuex 的 state 是一个用于存储应用级状态的对象,它包含了应用所需的所有数据。这些数据可以是任何类型,例如数组、对象或简单数据类型。

  • 集中管理:state 使得所有组件共享的数据集中管理成为可能,便于在不同组件之间共享和维护数据。
  • 响应式更新:Vuex 的状态是响应式的,当 state 中的数据发生变化时,所有依赖这些数据的组件都会自动更新。
  • 示例:
    const store = new Vuex.Store({
      state: {
        count: 0
      },
      getters: {
        doubleCount: state => state.count * 2
      }
    });
    

3.2 Getters

getters 是用于从 state 中派生出一些状态的方法,它们可以认为是 state 的计算属性。

  • 描述性名称:getters 通过描述性名称提供对 state 的读取,使得在组件中使用时更加清晰。
  • 组件内使用:在组件中可以通过 this.$store.getters 访问 getters,或者使用 mapGetters 辅助函数将 getters 映射到组件的计算属性。
  • 示例:
    computed: {
      ...mapGetters([
        'doubleCount',
      ])
    }
    

3.3 Mutations

mutations 是用于修改 state 的唯一方法。它们是同步的事务操作,当 state 需要变更时,必须通过 mutations 来完成。

  • 同步操作:mutations 必须是同步函数,这样 Vuex 才能追踪到状态的变化。
  • 变更状态:在组件中通过 this.$store.commit('mutationName') 触发 mutations,从而更新 state
  • 示例:
    store.commit('increment');
    

3.4 Actions

actions 类似于 mutations,不同之处在于它们可以包含异步操作。actions 通过 mutations 来间接修改 state

  • 异步操作:actions 可以执行异步操作,例如 API 请求,并在操作完成后提交 mutations
  • 组件内分发:在组件中通过 this.$store.dispatch('actionName') 分发 actions
  • 示例:
    store.dispatch('incrementAsync');
    

Vuex 状态管理提供了一种强大的方式来管理复杂的应用状态,通过集中式存储和严格的规则使得状态管理变得可预测和易于维护。

4. 属性与事件传递

4.1 $attrs

$attrs 是 Vue.js 提供的一个对象,包含了父作用域中不被 prop 所识别的特性绑定(class 和 style 除外)。当组件没有声明任何 prop 时,$attrs 会包含所有的父作用域绑定(class 和 style 除外),并且可以通过 v-bind 传递给子组件。

  • 透传属性:在多层嵌套的组件中,有时需要将某些属性传递给更深层次的子组件,这时可以使用 $attrs 来实现属性的透传。
  • 示例:
    <template>
      <child-component v-bind="$attrs"></child-component>
    </template>
    
  • 配合 inheritAttrs:组件默认会将未声明的属性绑定到自身的根元素上,通过设置 inheritAttrs: false,可以阻止这一行为,然后通过 $attrs 手动传递给子组件。

4.2 $listeners

$listeners 是一个对象,包含了父作用域中的(不含 .native 修饰器的)v-on 事件监听器。它可以通过 v-on 传递给子组件。

  • 事件透传:类似于 $attrs$listeners 允许我们将事件监听器从一个组件传递到另一个组件,特别是当需要将事件监听器传递给深层子组件时。
  • 示例:
    <template>
      <child-component v-on="$listeners"></child-component>
    </template>
    
  • 避免重复:使用 $listeners 可以避免在子组件中重复编写事件监听器,使得父组件能够统一管理事件监听。

通过 $attrs$listeners,Vue.js 提供了一种灵活的方式来传递属性和事件,使得组件间的通信更加简洁和高效。这些特性特别适用于组件库的开发者,他们可以构建出更加灵活和可复用的组件。

5. Provide/Inject API

5.1 Provide

provide 是 Vue.js 2.2.0 版本引入的一个 API,允许祖先组件向所有子孙组件提供数据。无论组件层次结构有多深,只要组件间存在祖先与后代的关系,provide 就能够起作用。

  • 数据提供:祖先组件通过 provide 函数定义可提供给后代的数据或方法。
  • 示例:
    provide() {
      return {
        theme: 'dark', // 提供一个 theme 数据
        toggleTheme: this.toggleTheme // 提供一个方法
      };
    }
    

5.2 Inject

provide 相对应,inject 允许子孙组件接收来自祖先组件提供的数据。

  • 数据注入:子孙组件通过 inject 选项接收祖先组件提供的数据。
  • 示例:
    inject: ['theme', 'toggleTheme'], // 接收 theme 数据和 toggleTheme 方法
    
  • 响应式:需要注意的是,provideinject 本身不是响应式的。如果需要响应式,可以借助 Vue.observable 对象。

通过 provideinject,Vue.js 实现了一种在组件树中跨级传递数据的方式,这在某些场景下比事件总线或 Vuex 更为便捷和高效。然而,过度使用 provideinject 可能会导致组件之间的耦合度增加,因此建议在确实需要时才使用此特性。

6. 直接访问实例

6.1 p a r e n t / parent/ parent/children

在 Vue.js 中,每个组件实例都有 $parent$children 属性,它们分别用于访问当前组件的父组件和所有子组件。

  • 访问父组件:通过 $parent 属性,子组件可以直接访问其父组件的实例,从而能够访问父组件的数据和方法。

  • 示例:

    // 子组件中访问父组件的数据
    console.log(this.$parent.someData);
    
  • 访问子组件:通过 $children 属性,父组件可以访问其所有子组件的实例数组。这允许父组件直接与子组件通信或调用子组件的方法。

  • 示例:

    // 父组件中调用第一个子组件的方法
    this.$children[0].someMethod();
    
  • 局限性:虽然 $parent$children 提供了一种直接的组件间通信方式,但它们也有一些局限性。例如,它们不支持响应式更新,且过度依赖这种直接访问可能会使组件之间的耦合度增加,从而影响组件的可维护性和可复用性。

  • 替代方案:在许多情况下,推荐使用 props、事件、事件总线、Vuex 或 Provide/Inject API 等更声明式的方法进行组件间通信,以保持组件的独立性和可维护性。直接访问实例的方法更适合于那些需要快速访问特定组件,且不会引起紧耦合的场景。

7. 插槽通信

7.1 插槽基础

插槽(Slots)是 Vue.js 中用于组件内容分发的机制,它允许开发者在组件的模板中预留一个或多个位置,这些位置可以被组件的使用者填充自定义的内容。

  • 内容分发:插槽允许将内容从父组件传递到子组件的特定位置,这为构建可复用组件提供了强大的灵活性。
  • 示例:
    <template>
      <div>
        <slot>默认内容</slot>
      </div>
    </template>
    

7.2 具名插槽

当组件需要多个插槽时,可以使用具名插槽来为每个插槽提供不同的名称。

  • 具名插槽:通过 slot 属性为插槽命名,使得父组件可以指定内容应该插入到哪个插槽中。
  • 示例:
    <template>
      <div>
        <slot name="header">默认头部</slot>
        <slot name="footer">默认底部</slot>
      </div>
    </template>
    

7.3 作用域插槽

作用域插槽允许子组件将数据作为插槽的一部分传递回父组件,从而实现更动态的内容分发。

  • 作用域插槽:子组件通过 slot-scope 属性接收数据,并将数据作为插槽的一部分传递给父组件。
  • 示例:
    <template>
      <ul>
        <li v-for="item in items" :key="item.id">
          <slot :item="item">{{ item.defaultText }}</slot>
        </li>
      </ul>
    </template>
    

7.4 插槽的优缺点

插槽提供了一种强大的方式来构建具有高度可定制性的组件,但它们也有一些局限性。

  • 优点:插槽使得组件更加灵活和可复用,开发者可以根据需要插入自定义内容,包括布局和行为。
  • 缺点:过度使用插槽可能会使组件的模板变得复杂,难以理解和维护。此外,插槽不支持动态绑定,这限制了它们的使用场景。

插槽是 Vue.js 提供的一种声明式的内容分发 API,它为构建复杂的组件提供了强大的工具,但开发者应该根据具体场景谨慎使用。

  • 29
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值