Skip to content

组件通信

Vue的组件通信主要分为父子组件通信和跨组件通信。

父子组件通信

props/$emit

最常见的通信方式,父组件通过props向子组件传递数据,子组件通过$emit触发自定义事件通知父组件。

$attrs

如果父组件传递了一些props到子组件,但子组件没有声明这些props,则称它们为attribute,这些属性会直接附着在子组件的根元素上。不包括class和style,它们会被特殊处理。

示例:

vue
<!-- 子组件 -->
<template>
  <div>
    <h1>{{ msg }}</h1>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },
  created() {
    console.log(this.$attrs); // { "data-a": "1", "data-b": "2" }
  },
}
</script>
vue
<!-- 父组件 -->
<template>
  <div id="app">
    <!-- 除 msg 外,其他均为 attribute -->
    <HelloWorld
      data-a="1"
      data-b="2"
      msg="Welcome to Your Vue.js App"
    />
  </div>
</template>

<script>
import HelloWorld from "./components/HelloWorld.vue";

export default {
  components: {
    HelloWorld,
  },
}
</script>

渲染结果:

html
<div id="app">
  <div data-a="1" data-b="2">
    <h1>Welcome to Your Vue.js App</h1>
  </div>
</div>

子组件可以通过在选项中配置inheritAttrs: false,禁止将attribute附着在子组件的根元素上,但不影响通过$attrs获取attribute

$listeners

子组件可以通过$listeners获取父组件传递过来的所有事件处理函数,如果我们有需要对一些ui组件进行二次封装时,就可以通过$attr和$listeners来将用户的属性和事件传递给ui组件。

示例:

vue
<template>
  <el-table
    v-bind="$attrs"
    v-on="$listeners">
    <slot></slot>
  </el-table>
</template>

v-model

用于给表单控件或组件进行双向数据绑定。

sync修饰符

sync修饰符类似于v-model,用于双向数据绑定,不同的是,v-model只能绑定一个数据,sync修饰符可以绑定多个。当我们希望子组件注册的事件只是修改属性时,就可以使用sync修饰符。

vue
<!-- 子组件 -->
<template>
  <div>
    <p>
      <button @click="$emit(`update:num1`, num1 - 1)">-</button>
      {{ num1 }}
      <button @click="$emit(`update:num1`, num1 + 1)">+</button>
    </p>
    <p>
      <button @click="$emit(`update:num2`, num2 - 1)">-</button>
      {{ num2 }}
      <button @click="$emit(`update:num2`, num2 + 1)">+</button>
    </p>
  </div>
</template>

<script>
export default {
  props: ["num1", "num2"],
};
</script>
vue
<template>
  <div id="app">
    <Numbers :num1.sync="n1" :num2.sync="n2" />
    <!-- 等同于 -->
    <Numbers
      :num1="n1"
      @update:num1="n1 = $event"
      :num2="n2"
      @update:num2="n2 = $event"
    />
  </div>
</template>

<script>
import Numbers from "./components/Numbers.vue";

export default {
  components: {
    Numbers,
  },
  data() {
    return {
      n1: 0,
      n2: 0,
    };
  },
};
</script>

$parent 和 $children

通过$parent和$children可以获取当前组件的父组件实例和子组件实例。

slot

插槽能够让父组件访问子组件抛出的数据

vue
<!-- 子组件 -->
<template>
  <span>
    <slot v-bind:user="user">
      {{ user.lastName }}
    </slot>
  </span>
</template>
vue
<!-- 父组件 -->
<template>
  <current-user>
    <template v-slot:default="slotProps">
      {{ slotProps.user.firstName }}
    </template>
  </current-user>
</template>

ref

通过ref可以获取子组件实例或DOM元素。

跨组件通信

provide/inject

provide/inject只适用于祖先组件与其子孙后代组件通信。

js
// 父级组件提供 'foo'
var Provider = {
  provide: {
    foo: 'bar'
  },
  // ...
}

// 子组件注入 'foo'
var Child = {
  inject: ['foo'],
  created () {
    console.log(this.foo) // => "bar"
  }
  // ...
}

router

如果一个组件改变了地址栏,所有监听地址栏的组件都会做出相应反应。

最常见的场景就是通过点击router-link组件改变了地址,router-view组件就渲染其他内容。

vuex

适用于大型项目的状态管理工具。

eventbus

组件通知事件总线发生了某件事,事件总线通知其他监听该事件的所有组件运行某个函数。