[TOC]
Communication among components
# 父向子组件通信
## 通过属性传值 props
```
// 父组件 List.vue
...
<List-item :obj="obj" :arr="arr" :func="func"></List-item>
...
// 子组件 ListItem.vue
<template>
<div>
<div>{{msg}}</div>
<div>{{obj}}</div>
<div>{{arr}}</div>
</div>
</template>
<script>
export default {
props: {
obj: Object, // props 是对象
arr: Array, // props 是数组
func: Function // props 是 方法
},
created() {
this.func()
}
}
</script>
```
## 通过 $parent 获取父组件实例的方法或者属性
从严格意思上讲不是值的传递,而是一种"取"(不推荐直接通过实例进行值的获取)。
可以通过 Vue 的[实例属性](https://cn.vuejs.org/v2/api/#vm-parent)`$parent`获得父组件的实例,借助实例可以调用父实例中的方法,或者获取父实例上的属性,从而达到取值的目的。
```
// 子组件
...
this.data = this.$parent.sendMessage(); // 调用父实例中的方法
this.msg = this.$parent.msg; // 获取父实例中的属性
...
```
## provide(生产者)和inject(注入)-- 深层次嵌套组件传值
***`provide`和`inject`主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。***
生产(**provide**)数据,子组件通过注入(**inject**)来消费数据,感觉跟 react 的`React.createContext`很像,一个生产者一个消费者,来跨组件传递数据。
父组件:
```js
provide(){
return{
showName:function(){
console.log("父组件数据!!!!!!!!!!!!!!!!");
}
}
}
```
任何后代组件:
```js
inject: ['showName'],
created () {
console.log(this.showName());
}
```
> 然而,依赖注入还是有负面影响的。它将你应用程序中的组件与它们当前的组织方式耦合起来,使重构变得更加困难。同时所提供的属性是非响应式的。这是出于设计的考虑,因为使用它们来创建一个中心化规模化的数据跟使用 `$root` 做这件事都是不够好的。
> 如果你想要共享的这个属性是你的应用特有的,而不是通用化的,或者如果你想在祖先组件中更新所提供的数据,那么这意味着你可能需要换用一个像 Vuex 这样真正的状态管理方案了。
## vue 2.4 中`$attrs` 和 `$listeners`
# 子向父组件通信
## 通过事件传值 $emit
* 子组件使用`$emit`发送一个自定义事件,事件名称是一个字符串。
* 父组件使用指令`v-on`绑定子组件发送的自定义事件。
```
// 父组件 List.vue
<template>
<div>
<!-- 监听自定义事件 -->
<List-item v-on:welcome="getWelcome"></List-item>
</div>
</template>
<script>
import ListItem from "./List-item";
export default {
components: {
ListItem
},
methods: {
getWelcome(data) {
alert(data)
}
}
}
</script>
// 子组件 ListItem.vue
<template>
<button @click="handleClick">Click me</button>
</template>
<script>
export default {
methods: {
handleClick() {
// 使用 $emit 发送自定义事件 welcome
this.$emit('welcome', 'hello');
}
}
}
</script>
```
## 通过 $children 获取子组件实例
此方式同`$parent`...。
## 通过 ref 注册子组件引用
尽管存在`prop`和事件,有的时候你仍可能需要在 JavaScript 里直接访问一个子组件。为了达到这个目的,可以通过`ref`特性为这个子组件赋予一个 ID 引用。
```
<template>
<div>
<List-item ref="item" :title="title"></List-item>
<div>{{data}}</div>
</div>
</template>
<script>
import ListItem from "./List-item";
export default {
data() {
return {
title: "我是title",
data: ""
}
},
components: {
ListItem
},
mounted() {
this.data = this.$refs.item.message;
}
}
</script>
```
[更多 ref 用法](https://cn.vuejs.org/v2/guide/components-edge-cases.html#%E8%AE%BF%E9%97%AE%E5%AD%90%E7%BB%84%E4%BB%B6%E5%AE%9E%E4%BE%8B%E6%88%96%E5%AD%90%E5%85%83%E7%B4%A0)
# 兄弟组件通信
## event bus
非父子组件怎么通过事件进行同步数据,或者同步消息呢?
Vue 中的事件触发和监听都是跟一个具体的 Vue 实例挂钩。 所以在不同的 Vue 实例中想进行事件的统一跟踪和触发,那就需要一个公共的 Vue 实例,这个实例就是公共的事件对象。
创建一个公共的 bus,然后导出一个 bus
bus.js:
~~~
import Vue from 'vue';
export const bus = new Vue();
~~~
然后A B两个组件同时引入 bus.js
~~~
import { bus } from '@/components/bus.js';
~~~
A 组件 $emit 事件及要传的值
~~~
getBrother(){
bus.$emit('testEvent','我是要传的值')
}
~~~
B组件接收
~~~
bus.$on('testEvent',val => {
console.log(val);
this.val = val;
})
~~~
当然了也可以直接引入 [vue-bus](https://npm.io/package/vue-bus)
~~~
$ yarn add vue-bus
$ npm install vue-bus --save
~~~
----
借助 `$root`:
```
<script>
export default {
name: 'Car',
methods: {
handleClick: function() {
this.$root.$emit('clickedSomething')
}
}
}
</script>
```
然后在组件中的生命周期 `mounted` 注册监听事件:
```
<script>
export default {
name: 'App',
mounted() {
this.$root.$on('clickedSomething', () => {
//...
})
}
}
</script>
```
## 通过父组件进行过渡
父组件只是充当一个中间媒介,这种方法耦合度高,*不是方法的方法:*
1. 子组件`A`通过事件`$emit`传值传给父组件。
2. 父组件通过属性`props`传值给子组件`B`。
# 其他
官网中提供一个指令[v-slot](https://cn.vuejs.org/v2/guide/components-slots.html),它与`props`结合使用从而达到插槽后备内容与子组件通信的目的。
我们首先需要在子组件的`slot`中传递一个`props`(这个`props`叫做插槽 props),这里我们起名叫`user`:
~~~
<!-- 子组件 current-user.vue -->
<template>
<div>
<div>current-user组件</div>
<slot :user="user">
插槽里默认显示:{{user.firstName}}
</slot>
</div>
</template>
~~~
在父组件中,包含插槽后备内容的子组件标签上我们绑定一个`v-slot`指令,像这样:
```
<template>
<div>
<div>我是Users组件</div>
<!-- slotProps里的内容就是子组件传递过来的 props -->
<!-- "user": { "firstName": "zhao", "lastName": "xinglei" } -->
<current-user v-slot="slotProps">
{{ slotProps }}
</current-user>
</div>
</template>
```
还可以使用 localstorage, Vuex 状态管理库,或者是一些订阅发布的库[pubsub-js](https://npm.io/package/pubsub-js)。
# 跨组件通信
[mitt: 🥊 Tiny 200 byte functional event emitter / pubsub.](https://github.com/developit/mitt)
# 参考
[Vue 组件间通信 12 种方法汇总](https://segmentfault.com/a/1190000020796713)
[Vue 组件通信方式全面详解](https://libin1991.github.io/2019/02/03/Vue%E7%BB%84%E4%BB%B6%E9%80%9A%E4%BF%A1%E6%96%B9%E5%BC%8F%E5%85%A8%E9%9D%A2%E8%AF%A6%E8%A7%A3/)
- Introduction
- Introduction to Vue
- Vue First App
- DevTools
- Configuring VS Code for Vue Development
- Components
- Single File Components
- Templates
- Styling components using CSS
- Directives
- Events
- Methods vs Watchers vs Computed Properties
- Props
- Slots
- Vue CLI
- 兼容IE
- Vue Router
- Vuex
- 组件设计
- 组件之间的通信
- 预渲染技术
- Vue 中的动画
- FLIP
- lottie
- Unit test
- Vue3 新特性
- Composition API
- Reactivity
- 使用 typescript
- 知识点
- 附录
- 问题
- 源码解析
- 资源