# Vue 组件通信

{% hint style="info" %}
项目地址：[Vue 组件通信](https://github.com/MrEnvision/Front-end_learning_project/tree/master/vue_components_communication)，参考资料：[「Vue 通信六种方式」](https://segmentfault.com/a/1190000019208626)
{% endhint %}

## 1. 组件关系

![](https://1936115583-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-ME8bAQhIbNQVAgxp8HX%2Fuploads%2Fgit-blob-b2c250114f1b94d30988736df5e2cd857a259424%2F%E7%BB%84%E4%BB%B6.png?alt=media)

* 父子组件：A 与 B、B 与 C、B 与 D、C 与 E、D 与 E
* 子孙组件：A 与 D、B 与 E
* 兄弟组件：C 与 D
* 隔代组件：A 与 E

## 2. 组件通讯

| 方法                     | 父子通信 | 兄弟通信 | 跨级通信 |
| ---------------------- | :--: | :--: | :--: |
| props/emit/sync        |  ☑️  |  ✖️  |  ✖️  |
| Vuex（工程项目最常用方式）        |  ☑️  |  ☑️  |  ☑️  |
| 中央事件总线 Bus             |  ☑️  |  ☑️  |  ☑️  |
| $attrs/$listeners      |  ☑️  |  ✖️  |  ☑️  |
| provide/inject         |  ☑️  |  ✖️  |  ☑️  |
| $ref/$parent/$children |  ☑️  |  ✖️  |  ✖️  |

### 2.1 props/emit/sync

父组件向子组件传值：props

```markup
<!-- 父组件 -->
<template>
  <child :msg='myMsg'></child>
</template>
<script>
import Child from "./components/Child"
export default {
  components:{ Child },
  data(){
    return{
      myMsg: 'hello child'
    }
  }
}
</script>


<!-- 子组件 -->
<template>
  <div>{{ myMsg }}</div>
</template>
<script>
export default {
  props: {
    myMsg: { // hello child
      type: String,
      default: ''
    }
  }
}
</script>
```

子组件向父组件传值：绑定事件 + emit

```markup
<!-- 父组件 -->
<template>
  <child @getMsg='updateMsg'></child>
</template>
<script>
import Child from "./components/Child"
export default {
  components:{ Child },
  methods: {
    updateMsg: (val) => {
      console.log(val) // hello parent
    }
  }
}
</script>


<!-- 子组件 -->
<template>
  <button @click='onClick'>点击</button>
</template>
<script>
export default {
  methods: {
    onClick: () => {
      this.$emit('getMsg', 'hello parent')
    }
  }
}
</script>
```

通过 sync 使得子组件更新父组件属性

```markup
<!-- 父组件 -->
<template>
  <child :msg.sync='myMsg'></child>
</template>
<script>
import Child from "./components/Child"
export default {
  components:{ Child },
  data(){
    return{
      myMsg: 'hello child'
    }
  }
}
</script>


<!-- 子组件 -->
<template>
  <button @click='onClick'>点击</button>
</template>
<script>
export default {
  methods: {
    onClick: () => {
      this.$emit('update:myMsg', 'hello parent')
    }
  }
}
</script>
```

### 2.2 Vuex

这是最常用的方式，具体详见[「官方文档」](https://vuex.vuejs.org/zh/)和[「项目工程」](https://github.com/MrEnvision/Front-end_learning_project/tree/master/vuex_tutorial)。

### 2.3 中央事件总线 Bus

```javascript
// bus.js - 创建实例
import Vue from 'vue';
export default new Vue();

// 调用事件
import Bus from 'bus.js'
Bus.$emit('getTarget', 'hello world!');

// 注册事件
import Bus from 'bus.js'
Bus.$on('getTarget', target => {  
    console.log(target);  
});
```

### 2.4 $attrs/$listeners

A组件嵌套B，B组件嵌套C；A传递属性到B，B可以通过this.$attrs获取，并且要继续传递给下一层，就需要在下一层组件上绑定`v-on='$attrs'`，注意如果B中通过props接收相同名称的属性， this.$attrs此时以及后续都不能读取到该名称属性。$listeners传递方法与$attrs传递属性同理。

### 2.5 provide/inject

祖先组件中通过provider来提供变量，然后在子孙组件中通过inject来注入变量。**provide 和 inject 绑定并不是可响应的**，provide与inject 实现数据响应式可详见[「Vue 通信六种方式」](https://segmentfault.com/a/1190000019208626)（Vue.observable 优化响应式 provide）。

```javascript
export default {
  provide: {
    name: 'hello'
  }
}
```

```javascript
// B.vue
export default {
  inject: ['name'], // 通过this.name获取
  mounted () {
    console.log(this.name);  // hello
  }
}
```

### 2.6 $ref/$parent/$children

对组件进行`ref`命名，则可以通过`this.$refs.名字`来获取并调用其相应的方法和获取相应的数据，而`this.$parent`和`this.$children`同样能够获取完整的vue组件信息。

{% hint style="info" %}
如果你对内容有任何疑问，欢迎提交 [❕issues](https://github.com/MrEnvision/Front-end_learning_notes/issues) 或 [✉️ email](mailto:EnvisionShen@gmail.com)
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://sherwinshen.gitbook.io/qian-duan-xue-xi/frontend-framework/vue/vue-components-communication.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
