Vue组件 通信的九种方式

442 阅读3分钟

我自己只总结了9种方式,欢迎同学讨论、补充。 : )

一、Props向子组件传递数据 ----父向子通讯

​ 父组件这样绑定给子组件,子组件就可以在Props中接受

父组件

<son :list="MyData"></son>

子组件

props: {
  list: {
    type: Object,
    default: () => {}
  }
}

二、$emit使用-----子向父通讯

父组件

<son @updateName="changeName">

子组件

这样子组件就可以通过this.$emit('updateName', 'Edon')来触发父组件的changeName

sync与v-model---- 父子互传

结合第一第二点通讯方式,我们下面来看看syncv-model

sync与v-model都可以进行父子组件的双向绑定,且两者都是语法糖

用sync进行组件双向绑定
父组件:
<son1 :test.sync="test"/>

这其实是语法糖,上面的写法会编译为下面这样

<son1 :test="test" @update:test="(val) => this.test = val"/>
子组件:
<template>
  <div>
    <div>Son test:{{ test }}</div>
    <button @click="addTestBySon">Son test Emit</button>
  </div>
</template>

<script>
export default {
  props: {
    test: {
      type: Number,
      default: -1
    }
  },
  methods: {
    addTestBySon() {
      // 这句是关键,因为是简写,所以更改值的时候一定要遵循update:xxx的写法
      this.$emit('update:test', this.test + 1)
    }
  }
};
</script>

用v-model进行双向绑定

父组件:
<son1 v-model="count" />
 // 上面这句话会编译为:
 <son1 :value="count" @input="(val)  => this.count = val">
子组件:
<template>
  <div>
    <div>Son value:{{ value }}</div>
    <button @click="addValueBySon">Son Value Emit</button>
  </div>
</template>

<script>
export default {
  name: 'Son1',
  props: {
    // 如果用了v-model,那么子组件接受的值只能为value
    value: {
      type: Number,
      default: -2
    }
  },
  methods: {
    addValueBySon() {
      // 如果用了v-model,那么触发的时间也是固定为input
      this.$emit('input', this.value + 1)
    },
  }
};
</script>


所以v-model,sync都可以完成父子组件值的双向绑定,那么如何选用?

首先,两则均是语法糖。当我们父子组件绑定的值为value的时候,用v-model.如果绑定的值为其他名字(不叫value),那么就用sync

三、$parent、 $children----父子相互通讯

子组件可以通过$parent获取父组件

父组件可以通过$children子组件,如果有多个子组件则为数组。

如果层级很深那么就会出现$parent、$parent.....我们可以封装一个$dispatch方法向上进行派发.$broadcast向下派发

Vue1.0有$boardcast(通知所有子组件触发方法)与$diapatch(通知所有父组件触发方法),vue2.0就删掉了。但是在一些组价库中,还是有这个方法,比如ElementUI

$dispatch 向上进行派发

Vue.prototype.$diapatch = function $diapatch(eventName, data){
  let parent = this.$parent
  while(parent) {
    parent.emit(eventName,data)
    parent = this.$parent
  }
}

$broadcast 向下派发

Vue.prototype.$broadcast = function $broadcast(eventName, data) {
  const broadcast = (children) => {
    children.forEach(child => {
      child.$emit(eventName, data)
      if (child.$children) {
        broadcast(child.$children)
      }
    })
  }
  broadcast(this.$children)
}

四、$attrs、$listeners ----父向子通讯

$arrts

在父组件传给子组件值的时候,不光只是可以传注册的的属性(即子组件props注册的属性),也可以传子组件未注册的属性

比如,在父组件中,不是以v-bind的形似也可以给子组件传值

父组件

<compoentA name="Edon" age="88"></compoentA>

子组件就可以通过this.$attrs来获取一个对象: {name: "Edon", age: "88"}

但是这样,会把这些数据直接渲染到dom里面。

可以把数据显示在dom节点上,可能不是我们想要的效果

我们在子组件中添加inheritAttrs: false,这样在dom节点上,就看不到属性了,但是依然可以用$attrs去获得。

$listeners

<son1 @test="add"></son1>

当给子组件传方法的时候,在子组件就可以通过$listeners获取到方法。


除了在子组件可以用$arrts、$listners以外,还可以结合孙子组件使用

<Grandson v-bind="$attrs" v-on="$listeners"></Grandson>

这样就可以把Parent传给son的数据,在son中再传到grandson

五、provide、inject ---父向子通讯

类型

  • provideObject | () => Object
  • injectArray<string> | { [key: string]: string | Symbol | Object }

provideinject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。

一般在业务代码中,不建议使用。一般用在写一个库的时候使用

六、ref ---父子相互通讯

比如,下面代码可以获得grand2组件的属性

this.$refs.grand2.name

七、EventBus ---父子相互通讯

EventBus其实是一个Vue实例

Vue.prototype.$bus = new Vue();

触发事件

this.$bus.$emit("my", "我是Grandson1");

监听事件

  this.$bus.$on("my", data => {
   console.log(data);
  });

八、Vuex ---父子相互通讯

这里需要安装与配置Vuex,这里不做详诉,可以参考官网。Vuex官网

九、插槽 ---父子相互通讯

父传子

子组件

<a
  v-bind:href="url"
  class="nav-link"
>
  <slot></slot> // 父组件的Your Profile就会传到这里
</a>

父组件

<Son>
  Your Profile
</Son>

子传父

有时候在使用子组件的时候,我们需要在父组件中,访问到子组件经过处理的的数据,就要使用到作用域插槽

子组件(List)

<template>
  <div>
    <div v-for="(item, index) in list" :key="index">
      // 把子组件循环的结果item,以fruitName的形式传给父组件
      <slot :fruitName="item">默认值</slot> 
    </div>
  </div>
</template>

父组件

<list :list="fruits">
  <template v-slot="{fruitName}"> // 结构出fruitName
<h1>{{fruitName}}</h1>
  </template>
</list> -->

ElementUI大量使用了这种语法,比如在el-table中,使用了插槽的方式,获取到了子组件中的数据,存放在scope