[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
- 知識點
- 附錄
- 問題
- 源碼解析
- 資源