@TOC
前言:
寫在前面: vue已經(jīng)更新到V2.6.10版本(相信很快就會出3.0版本)偎捎,相信我們也遇到了需要組件之間通信的需求,除了主流的vuex狀態(tài)管理模式讳癌,還有哪些方式解決組件之間的通信的問題穿稳,接下來就由我一一介紹給大家;
一晌坤、vuex狀態(tài)管理模式
Vuex 是一個專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式()逢艘。它采用集中式存儲管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測的方式發(fā)生變化骤菠。具體介紹請轉(zhuǎn)vuex它改;
其數(shù)據(jù)流向如下:
使用:
// cdn
<script src="/path/to/vue.js"></script>
<script src="/path/to/vuex.js"></script>
// npm
npm install vuex --save
//yarn
yarn add vuex
- 使用
// /src/store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// /src/store.js
/**
* 狀態(tài)樹
*/
const state = {
count: 0
}
/**
* 和組件計算屬性一樣, store 的計算屬性
* getter 的返回值會根據(jù)它的依賴被緩存起來商乎,且只有當(dāng)它的依賴值發(fā)生了改變才會被重新計算
*/
const getters = {
getCount (state) {
return state.count || 0
}
}
/**
* Vuex 中的 mutation 非常類似于事件:
* 更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation央拖。
* 每個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調(diào)函數(shù) (handler)
*/
const mutations = {
mutaCount (state, payload) {
state.count = state.count + 1
}
}
/**
* Action 類似于 mutation,不同在于:
* Action 提交的是 mutation鹉戚,而不是直接變更狀態(tài)鲜戒。
* Action 可以包含任意異步操作。
*/
const actions = {
actCount ({commit}, payload) {
commit('mutaCount', payload)
}
}
const store = new Vuex.Store(
{
state,
getters,
mutations,
actions
}
)
export default store
// src/main.js
...
import store from './store'
/* eslint-disable no-new */
new Vue({
...
store,
...
})
以上已經(jīng)把vuex注入到vue實(shí)例抹凳;
組件中使用
// src/components/vuexOne.vue
//...
methods: {
// ...mapActions(['actCount']), // 輔助函數(shù)方式使用遏餐,需要組件import {mapActions} from 'vuex'
addCount () {
// this.actCount()
this.$store.dispatch('actCount')
}
}
//...
// src/components/vuexTwo.vue
//...
computed: {
// ...mapGetters(['getCount']), // 輔助函數(shù)方式使用,需要組件import {mapGetters} from 'vuex'
// 常規(guī)方式使用
getCount () {
return this.$store.getters.getCount
}
}
//...
此時: 觸發(fā)vuexOne.vue的addCount時間赢底,vuexTwo.vue的頁面能改更新失都;這就是vuex的簡單使用;如需了解 模塊module 及其他 輔助函數(shù) 等可以閱讀文檔vuex
效果如下:
總結(jié): Action和Mutation兩者的功能很相似 幸冻,并且很多時候粹庞,我們只需要在組件中通過this.$store.commit('xxx') 或者 mapMutations輔助函數(shù)來使用 Mutation 直接更新state的數(shù)據(jù),而不需要通過 Action 這一步洽损,但 Action 和 Mutation 有個非常大的區(qū)別就是: Mutation 必須是同步函數(shù)(因?yàn)閙utation 中混合異步調(diào)用會導(dǎo)致你的程序很難調(diào)試庞溜,所以在此限制為只能進(jìn)行同步),而Action 可以包含任意異步操作 趁啸。
二强缘、EventBus
EventBus 的實(shí)現(xiàn)原理是通過一個空的vue實(shí)例作為事件中心督惰,通過它來觸發(fā)事件($emit) 和監(jiān)聽事件($on), 巧妙而輕量地實(shí)現(xiàn)了任何組件間的通信; (適合少而小的項(xiàng)目使用不傅,如果有大量通信,依舊推薦vuex)
使用:
首先在utils創(chuàng)建一個新的vue實(shí)例赏胚,用作 事件中心
// utils/eventbus.js
...
import Vue from 'vue'
export default new Vue({
name: 'EventBus'
})
eventBusOne組件引入:
<template>
<div class="eventBusOne">
<div class="add-count-button-box">
<div>我是eventBusOne組件:</div>
<div class="add-count-button" @click="addCount">state++</div>
</div>
</div>
</template>
<script>
import eventBus from '../../../utils/eventBus'
export default {
name: 'EventBusOne',
data () {
return {
count: 1
}
},
methods: {
addCount () {
this.count += 1
eventBus.$emit('data-count', this.count)
}
}
}
</script>
eventBusTwo組件監(jiān)聽:
<template>
<div class="eventBusTwo">
<div class="add-count-button-box">
<div>我是eventBusTwo組件:</div>
<div>{{count}}</div>
</div>
</div>
</template>
<script>
import eventBus from '../../../utils/eventBus'
export default {
name: 'EventBusTwo',
data () {
return {
count: 1
}
},
computed: {
},
mounted () {
eventBus.$on('data-count', data => {
this.count = data
})
}
}
</script>
效果如下:
總結(jié): eventBus 原理 是利用一個空的vue實(shí)例當(dāng)做一個事件中心访娶,通過其分發(fā)及監(jiān)聽事件來傳遞數(shù)據(jù),也可以實(shí)現(xiàn)任何組件間的通信觉阅,包括父子崖疤、兄弟秘车、跨級等。但當(dāng)使用過多容易造成命名沖突劫哼,因此不利于大項(xiàng)目使用(當(dāng)大項(xiàng)目使用時叮趴,依舊推薦vuex)
ps:以上是目前使用比較多的可以跨組件包括兄弟組件通信的方法,接下來講其他有短板的方法权烧,有興趣的可以花幾分鐘繼續(xù)往下了解眯亦,否則客官可以止步于此,以免浪費(fèi)您寶貴的時間 ...
三般码、使用最多之 props與$emit
props 由父組件A往子組件B傳遞數(shù)據(jù)妻率,當(dāng)然還可以繼續(xù)組件B仍然可以往C組件(A的孫組件)繼續(xù)往下傳遞,
使用 propsOne(父組件)
<template>
<div class="propsOne">
<div class="add-count-button-box">
<div>我是propsOne組件:</div>
<div class="add-count-button" @click="addCount">count++</div>
<div class="add-count-button" @click="addState">state++</div>
</div>
<propsTwo v-model="count" :state="state" @addCount="twoAddCount" @addState="twoAddState"></propsTwo>
</div>
</template>
<script>
import propsTwo from './propsTwo'
export default {
name: 'PropsOne',
components: {
propsTwo
},
data () {
return {
count: 1,
state: 1
}
},
methods: {
addCount () {
this.count += 1
},
addState () {
this.state += 1
},
twoAddCount (value) {
this.count = value
},
twoAddState (value) {
this.state = value
}
}
}
</script>
使用 propsTwo(子組件)
<template>
<div class="propsTwo">
<div>count:{{value}}</div>
<div class="state">state:{{state}}</div>
<div>我是 propsTwo組件: </div>
<div class="add-count-button" @click="addCount">count++</div>
<div class="add-count-button" @click="addState">state++</div>
</div>
</template>
<script>
export default {
name: 'PropsTwo',
props: {
value: {
type: Number,
default: 1
},
state: {
type: Number,
default: 1
}
},
data () {
return {
}
},
methods: {
addCount () {
let count = this.value
count++
this.$emit('addCount', count)
},
addState () {
let state = this.state
state++
this.$emit('addState', state)
}
}
}
</script>
效果如下:
總結(jié): props是單向數(shù)據(jù)流板祝,即只能從父級傳到子級宫静,子級改變,父級的值不會改變(用.sync修飾符修飾可以實(shí)現(xiàn)雙向數(shù)據(jù)綁定)券时,但v-model是雙向數(shù)據(jù)流孤里,即雙向綁定,子級改變這個值時革为,父級也會跟著改變扭粱;$emit傳值和上面第二種的eventbus的原理一致,不過是事件分發(fā)到父級震檩,父級可以監(jiān)聽琢蛤;想了解sync修飾符請轉(zhuǎn)vue.org
四、parent博其、$children
ref被用來給元素或子組件注冊引用信息。引用信息將會注冊在父組件的 $refs 對象上迂猴。如果在普通的 DOM 元素上使用慕淡,引用指向的就是 DOM 元素;如果用在子組件上沸毁,引用就指向組件
實(shí)例:
// ref.vue
<template>
<div class="ref">
<div class="add-count-button" @click="getCount">
獲取refTwo的count峰髓,其值為:{{count}}
</div>
<refOne ref="refOne"></refOne>
</div>
</template>
<script>
import refOne from '../components/refDemo/refOne'
export default {
name: 'Props',
components: {
refOne
},
data () {
return {
count: ''
}
},
methods: {
getCount () {
console.log('ref========', this.$parent) // Vue
console.log('ref========', this.$children) // refOne
this.count = this.$refs.refOne.getCount()
}
}
}
</script>
// refOne
<template>
<div class="refOne">
<refTwo ref="refTwo"></refTwo>
<refTwo></refTwo>
</div>
</template>
<script>
import refTwo from './refTwo'
export default {
name: 'RefOne',
components: {
refTwo
},
data () {
return {
count: 2
}
},
methods: {
getCount () {
console.log('refOne========', this.$parent) // refOne
console.log('refOne========', this.$children) // [refTwo,refTwo]
return this.$refs.refTwo.getCount()
}
}
}
</script>
// refTwo
<template>
<div class="refTwo">
<div class="add-count-button-box">
<div>我是refTwo組件的count ==== {{count}}</div>
</div>
</div>
</template>
<script>
export default {
name: 'RefTwo',
data () {
return {
count: 1000
}
},
methods: {
getCount () {
console.log('refTwo========', this.$parent) // refOne
console.log('refTwo========', this.$children) // []
return this.count
}
}
}
</script>
從上面的操作可知,通過ref調(diào)用子組件的方法息尺,可以把相應(yīng)的數(shù)據(jù)傳導(dǎo)到父級携兵;
特別地 $children拿到的當(dāng)前實(shí)例的直接子組件。需要注意 $children 并不保證順序搂誉,也不是響應(yīng)式的徐紧。如果你發(fā)現(xiàn)自己正在嘗試使用 $children 來進(jìn)行數(shù)據(jù)綁定,考慮使用一個數(shù)組配合 v-for 來生成子組件,并且使用 Array 作為真正的來源并级。
$parent拂檩、$children 從上述的打印,依舊可以發(fā)現(xiàn)$parent嘲碧、$children能拿到當(dāng)前組件的父級或者子級組件實(shí)例稻励,如果有多個,則為數(shù)組愈涩,如果為空钉迷,則為空數(shù)組,如果通過這個實(shí)例去拿相應(yīng)的屬性或者方法也是可行的 如下:
// ref
methods: {
getCount () {
console.log('ref========', this.$parent) // Vue
console.log('ref========', this.$children) // refOne
this.count = this.$refs.refOne.count // count 為 refOne的data里面的count ===2
}
}
ref請轉(zhuǎn)vue.org
$parent請轉(zhuǎn)vue.org
$children請轉(zhuǎn)vue.org
其他
如: provide與inject
provide 和 inject (Vue2.2.0新增API) 綁定 并不是可響應(yīng)的钠署。這是刻意為之的糠聪。然而,如果你傳入了一個可監(jiān)聽的對象谐鼎,那么其對象的屬性還是可響應(yīng)的舰蟆。
provide與inject 轉(zhuǎn)vue.org
如: $attrs/ $listeners
$attrs/ $listeners(Vue2.4增加) 版本在普通組件中,沒有被定義為 prop 的特性會自動添加到組件的根元素上狸棍,將已有的同名特性進(jìn)行替換或與其進(jìn)行智能合并身害。
$attrs/ $listeners轉(zhuǎn)vue.org
總結(jié):
萬能通信: vuex、eventBus
父子通信:$refs 草戈、 $parent塌鸯、$children、provide/inject唐片;