这篇文章上次修改于 281 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

vue组件间通信

vue中的组件就是可复用的vue实例,和new Vue({...})接收相同的选项,除了el是根实例特有的以外。

组件实例作用域是独立的,所以项目开发中实现组件间通信也是非常重要的。

1、props、$emit

开发中父子组件常用的一种方式,使用方法很简单,具体看代码。

// 父组件
<Child :title="'xiaoliu'" @chClick="input"></Child>
......
methods: {
    input() {
        console.log('father');
    }
}
......

// 子组件
<button @cick="toEmit">{{title}}</button> // ==> button的value是“xiaoliu"
.....
props: {
    title: {
        type: string,
        default: ''
    }
},
methods: {
    toEmit(){
        this.$emit('chClick'); // ==> 触发父组件方法,改变父组件的状态
    }    
}

2、sync(同步)与v-modal

在某些情况下我们会需要对prop进行双向绑定,进行父子组件同步更新。

  • .syncupdate:myPropName 模式

    // 父组件 
    <Son :title="title" @update:title="title => this.title = title"></Son>
    // :title.sync  不能写表达式, 下面这种写法是上面的语法糖,实现的功能一样
    <Son :title.sync="title"></Son> 
    
    // 子组件   update:绑定的父组件data属性
    <button @click="$emit('update:title', 'ada')">更改</button>
  • v-model 默认会利用名为 value 的 prop 和名为 input 的事件。

    // 父组件 
    <Son :value="title" @input="title => this.title = title"></Son>
    // 下面写法是上面的语法糖
    <Son v-modal="title"></Son> 
    
    // 子组件 
    {{value}}  // 子组件接收的prop必须为value
    <button @click="$emit('input', 'ada')">更改</button> // $emit必须触发的是input事件

.sync相比于v-modal 更灵活,单一组件内表单一般使用v-modal和实例data进行双向绑定,组件间通信使用.sync较合适。

3、$parent、$children、$root

  • $parent 访问父组件的父组件,想要在往上访问记得多加$parent

    /* $parent 
       触发父组件方法时    this.$parent.XXX(options)
       触发父组件的父组件       this.$parent.$emit('xxxx', options);
       触发父组件的父组件的父组件  this.$parent.$parent.$emit('xxxx', options);
    */
    
    // 解决多个调用,访问最近的父组件
    Vue.prototype.$dispatch = function(event, value) {
        let parent = this.$parent;
        while(parent) {
           parent.$emit(event, value);
           parent = parent.$parent;
        }
    }
  • $children遍历当前组件有几个儿子组件(数组)

    // 遍历当前组件下所有子组件
    Vue.prototype.$broadcast = function (eventName, data) {
      const broadcast = function () {
        this.$children.forEach((child) => {
          child.$emit(eventName, data);
          if (child.$children) {
            $broadcast.call(child, eventName, data);
          }
        });
      };
      broadcast.call(this, eventName, data);
    };
  • $root 访问的是根父组件,也就是入口文件new的Vue实例

    // main.js
    new Vue({
      el: '#app',
      render: h => h(App),
      methods: {
        lant() {
          alert('111');
        }
      }
    })
    
    // 任意组件
    ......
    methods: {
        foo() {
            this.$root.lant(); // ==> 111
        }
    }
    ......

4、$attrs、$listeners

  • $attrs 得到父组件上所有不被prop所识别(classstyle除外)属性的集合,若继续往下传递用 v-bind=“$attrs”
  • $listeners 得到父组件所有(不包括.native修饰器)v-on 事件监听器的集合,若继续往下传递用 v-on="$listeners"

    // 父
    <Son name="xiaoliu" age="22" @click="()=>{this.title = 'efw'}"></Son>
    
    // 儿
    <!-- 可以在son组件中使用$listeners属性,可以将方法继续向下传递 -->
    <!-- 可以在son组件中使用$attrs属性,可以将属性继续向下传递 -->
    <div>
      儿子: {{$attrs.name}} // ==> "xiaoliu"
      <Grandson2 v-bind="$attrs" v-on="$listeners"></Grandson2>
    </div>
    
    // 孙
    <template>
     <div>
          孙子:{{$attrs}} // ==> {name="xiaoliu" age="22"}
          <button @click="()=>{this.title = 'efw'}"></button>
    </div>
    </template>

5、Provide 、 Inject

通过provide 向祖先组件注入变量,子组件无论层级有多深,都能使用inject 可以获取祖先组件注入的变量

  • provide 在父级中注入数据

    provide() {
    return { parentMsg: "父亲" };
    },
  • inject 在任意子组件中可以注入父级数据

    inject: ["parentMsg"] // 会将数据挂载在当前实例上

需要注意的是这种通信方式不是响应式的,就是当祖先组件状态变化时,通过inject注入的状态不会发生改变。可以通过将祖先实例对象注入到provide中,这样可以监听到祖先状态的改变,然后对当前组件进行相应的状态处理。

6、EventBus($on/\$emit)

先说说$on$emit的原理

  • vm.$on( event, callback )监听当前vm实例上的自定义事件。callback 回调处理$emit所传的参数。
  • vm.$emit( event, […args] ) 触发当前vm实例上的事件。参数都会传给$on监听器callback 回调。

用于跨组件通信(不复杂的项目可以使用这种方式),原理就是在Vue原型对象上new一个Vue实例,这样通过$on/$emit搭配就能实现跨组件通信。

Vue.prototype.$bus = new Vue();
 mounted() {
  this.$bus.$on("my", data => {
   console.log(data); // ==> '我是Grandson1'
  });
 },
mounted() {
  this.$nextTick(() => {
   this.$bus.$emit("my", "我是Grandson1");
  });
}

建议按需引用

import Vue from 'vue';
const $bus = new Vue();
export default $bus;

7、vuex

一般项目都会用到,这里就不多做介绍了,详情见官网

总结

vue常见组件通信层级可以分为三类:

  • 父子通信: props和$emit;$parent和$children;provide / inject ​;$attrs/$listeners
  • 兄弟通信: EventBus;Vuex
  • 跨级通信: EventBus;Vuex;provide / inject 、$attrs/​$listeners