@TOCjavascript
寫在前面: vue已經更新到V2.6.10版本(相信很快就會出3.0版本),相信咱們也遇到了須要組件之間通訊的需求,除了主流的vuex狀態管理模式,還有哪些方式解決組件之間的通訊的問題,接下來就由我一一介紹給你們;html
Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式()。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。具體介紹請轉vuex;vue
// 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
/** * 狀態樹 */
const state = {
count: 0
}
/** * 和組件計算屬性同樣, store 的計算屬性 * getter 的返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生了改變纔會被從新計算 */
const getters = {
getCount (state) {
return state.count || 0
}
}
/** * Vuex 中的 mutation 很是相似於事件: * 更改 Vuex 的 store 中的狀態的惟一方法是提交 mutation。 * 每一個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調函數 (handler) */
const mutations = {
mutaCount (state, payload) {
state.count = state.count + 1
}
}
/** * Action 相似於 mutation,不一樣在於: * Action 提交的是 mutation,而不是直接變動狀態。 * 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,
...
})
複製代碼
以上已經把vuex注入到vue實例;java
組件中使用git
// src/components/vuexOne.vue
//...
methods: {
// ...mapActions(['actCount']), // 輔助函數方式使用,須要組件import {mapActions} from 'vuex'
addCount () {
// this.actCount()
this.$store.dispatch('actCount')
}
}
//...
複製代碼
// src/components/vuexTwo.vue
//...
computed: {
// ...mapGetters(['getCount']), // 輔助函數方式使用,須要組件import {mapGetters} from 'vuex'
// 常規方式使用
getCount () {
return this.$store.getters.getCount
}
}
//...
複製代碼
此時: 觸發vuexOne.vue的addCount時間,vuexTwo.vue的頁面能改更新;這就是vuex的簡單使用;如需瞭解 模塊module 及其餘 輔助函數 等能夠閱讀文檔vuexgithub
效果以下: vuex
總結: Action和Mutation二者的功能很類似 ,而且不少時候,咱們只須要在組件中經過this.$store.commit('xxx') 或者 mapMutations輔助函數來使用 Mutation 直接更新state的數據,而不須要經過 Action 這一步,但 Action 和 Mutation 有個很是大的區別就是: Mutation 必須是同步函數(由於mutation 中混合異步調用會致使你的程序很難調試,因此在此限制爲只能進行同步),而Action 能夠包含任意異步操做 。npm
EventBus 的實現原理是經過一個空的vue實例做爲事件中心,經過它來觸發事件($emit) 和監聽事件($on), 巧妙而輕量地實現了任何組件間的通訊; (適合少而小的項目使用,若是有大量通訊,依舊推薦vuex)api
首先在utils建立一個新的vue實例,用做 事件中心數組
// utils/eventbus.js
...
import Vue from 'vue'
export default new Vue({
name: 'EventBus'
})
複製代碼
<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>
複製代碼
<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>
複製代碼
總結: eventBus 原理 是利用一個空的vue實例當作一個事件中心,經過其分發及監聽事件來傳遞數據,也能夠實現任何組件間的通訊,包括父子、兄弟、跨級等。但當使用過多容易形成命名衝突,所以不利於大項目使用(當大項目使用時,依舊推薦vuex)
ps:以上是目前使用比較多的能夠跨組件包括兄弟組件通訊的方法,接下來說其餘有短板的方法,有興趣的能夠花幾分鐘繼續往下了解,不然客官能夠止步於此,以避免浪費您寶貴的時間 ...
props 由父組件A往子組件B傳遞數據,固然還能夠繼續組件B仍然能夠往C組件(A的孫組件)繼續往下傳遞,
<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>
複製代碼
<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>
複製代碼
總結: props是單向數據流,即只能從父級傳到子級,子級改變,父級的值不會改變(用.sync修飾符修飾能夠實現雙向數據綁定),但v-model是雙向數據流,即雙向綁定,子級改變這個值時,父級也會跟着改變;$emit傳值和上面第二種的eventbus的原理一致,不過是事件分發到父級,父級能夠監聽;想了解sync修飾符請轉vue.org
ref被用來給元素或子組件註冊引用信息。引用信息將會註冊在父組件的 $refs 對象上。若是在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;若是用在子組件上,引用就指向組件
實例:
// 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調用子組件的方法,能夠把相應的數據傳導到父級;
特別地 $children拿到的當前實例的直接子組件。須要注意 $children 並不保證順序,也不是響應式的。若是你發現本身正在嘗試使用 $children 來進行數據綁定,考慮使用一個數組配合 v-for 來生成子組件,而且使用 Array 做爲真正的來源。
$parent、$children 從上述的打印,依舊能夠發現$parent、$children能拿到當前組件的父級或者子級組件實例,若是有多個,則爲數組,若是爲空,則爲空數組,若是經過這個實例去拿相應的屬性或者方法也是可行的 以下:
// 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請轉vue.org $parent請轉vue.org $children請轉vue.org
如: provide與inject
provide 和 inject (Vue2.2.0新增API) 綁定 並非可響應的。這是刻意爲之的。然而,若是你傳入了一個可監聽的對象,那麼其對象的屬性仍是可響應的。 provide與inject 轉vue.org
如: $attrs/ $listeners
$attrs/ $listeners(Vue2.4增長) 版本在普通組件中,沒有被定義爲 prop 的特性會自動添加到組件的根元素上,將已有的同名特性進行替換或與其進行智能合併。 $attrs/ $listeners轉vue.org
萬能通訊: vuex、eventBus 父子通訊:$refs 、 $parent、$children、provide/inject;