經過閱讀了官方文檔和大量網友的博文,融入本身的想法,整理出對組件通訊的見解,第一次發文,若有不周敬請諒解哦~javascript
組件實例的做用域是孤立的,但組件間的聯繫和交互不可避免。當組件間進行數據交互時,組件通訊必不可少。
html
因而組件的通訊能夠分爲兩種狀況:vue
父子組件間通訊(父傳子,子傳父),非父子組件通訊。java
step1:父組件用prop自定義屬性傳遞屬性,prop有字面量語法和動態語法。字面量只能將數據按字符串的形式傳遞;動態語法相似於v-bind,能夠將父組件數據的實時變化傳遞給子組件。
vuex
step2:子組件須要用props來顯式地聲明prop。子組件props的寫法有兩種:
數組
父將函數用prop傳遞給子,子在調用這個函數時,將數據做爲參數傳遞給父組件app
<child2 :changeParent="changeMyself"></child2>複製代碼
methods: {
// 父組件改變自身數據的方法
changeMyself (chidData) {
this.parentChangeData = chidData
}
}複製代碼
子組件中:
export default {
// 子組件顯示聲明props父組件的函數
props: {
changeParent: {
requried: true, // 表明是否爲必傳
type: Function // 表明數據類型
}
},
data () {
return {
childData: '我要把數據傳給父'
}
}
}複製代碼
<button @click="changeParent(childData)">點我傳遞數據給父組件</button>複製代碼
子$emit自定義一個事件,第一個參數爲事件名,第二個參數爲數據。父v-on綁定一個事件監聽器,在綁定的method中以參數形式得到子組件的數據。異步
子組件中:
函數
methods: {
// 自定義事件$emit暴露數據,數據做爲參數
changeParent () {
this.$emit('change', this.childData)
}
}複製代碼
父組件中:
組件化
<!-- 父組件綁定監聽,獲得子組件數據--> <child3 @change="changeMyself"></child3>複製代碼
methods: {
// 父組件改變自身數據的方法
changeMyself (chidData) {
this.parentChangeData = chidData
}
}複製代碼
用ref對子組件進行標記,父組件直接經過this.$refs[子組件ref].[子組件屬性/方法]
來得到子組件的數據。
注意:"$refs 只在組件渲染完成後才填充,而且它是非響應式的。它僅僅做爲一個直接訪問子組件的應急方案——應當避免在模版或計算屬性中使用 $refs 。" 官網這麼說的,只是應急用,通常不推薦這樣使用
思考:單項數據流
Prop 是單向綁定的:當父組件的屬性變化時,將傳導給子組件,可是反過來不會。這是爲了防止子組件無心間修改了父組件的狀態,來避免應用的數據流變得難以理解。
另外,每次父組件更新時,子組件的全部 prop 都會更新爲最新值。這意味着你不該該在子組件內部改變 prop。若是你這麼作了,Vue 會在控制檯給出警告。
在兩種狀況下,咱們很容易忍不住想去修改 prop 中數據:
Prop 做爲初始值傳入後,子組件想把它看成局部數據來用;
Prop 做爲原始數據傳入,由子組件處理成其它數據輸出。
對這兩種狀況,正確的應對方式是:
定義一個局部變量,並用 prop 的值初始化它:
定義一個計算屬性,處理 prop 的值並返回:
單項數據流致使子組件不能直接去改動父組件的數據。但真實場景中,有不少時候須要在子組件中更改父組件的數據。
父有一個改變其自身數據的方法,用prop傳給子,子在須要改變父組件數據的時候調用該方法。(與回調參數方法一致)
子組件經過事件發送,$emit事件將數據傳給父組件,父組件監聽後本身執行改變自身數據的方法。(與子傳父的第二種方法一致)
本身想到的方法:父組件有一個改變自身數據的方法,子經過this.$parent.[父組件方法]直接調用父組件方法,將子組件的數據做爲參數傳遞給父組件從而改變父組件數據
父組件中:
methods: {
// 父組件改變自身數據的方法
changeMyself (chidData) {
this.parentChangeData = chidData
}
}複製代碼
子組件中:
export default {
data () {
return {
childData: '我是子要傳給父的數據'
}
},
methods: {
change () {
// 經過直接訪問的方法調用
this.$parent.changeMyself(this.childData)
}
}
}複製代碼
但以上的方法都是基於事件觸發的,不能保證子父組件的數據時刻同步
4.使用.sync綁定修飾符在父組件prop中顯示強調雙向綁定。子組件在watch中使用$emit(‘update:data’,newVal)監聽,更新父組件prop傳過來的數據,父組件不須要再監聽該update方法。
父組件中:
<!-- 顯式綁定.sync修飾符 --> <child6 :parentData.sync= "parentData"></child6>複製代碼
子組件中:
props: ['parentData']複製代碼
// 子組件進行數據監聽
watch: {
childData (newVal, oldVal) {
this.$emit('update:parentData', newVal)
}
}複製代碼
這裏的自定義事件與子傳父的自定義事件有一些不一樣。
<child6 :parentData.sync="parentData"></child6>
會被擴展爲:
<child6 :parentData"parentData" @update:parentData="val => parentData = val"></child6>
思考:
自定義事件發生時候運行的響應表達式是<child6 :parentData="parentData" @update:parentData="val => parentData = val"></child6>
中的 "val => bar = val"
。
在子傳父的「經過$emit事件從子組件向父組件中傳遞數據」 裏,自定義事件發生時候運行的響應表達式是:<child @chang="changeMyself"></child>
中的changeMyself
。
對前者, 表達式 val => bar = val
意味着強制讓父組件的數據等於子組件傳遞過來的數據, 這個時候,咱們發現父子組件的地位是平等的。 父能夠改變子(數據), 子也能夠改變父(數據)。
對後者, changeMyself
是在父組件中定義的, 在這個函數裏, 能夠對從子組件接受來的數據作任意的操做或處理, 決定權徹底落在父組件中, 也就是: 父能夠改變子(數據), 但子不能直接改變父(數據)!, 父中數據的變更只能由它本身決定。
有一個投機取巧的辦法:在父用prop傳數據給子時,若子組件中props的類型爲對象或數組時,能夠直接在子組件中修改這個prop來的數據,且不會被vue檢測報錯,但這樣會使得數據流變得更加難以分析,同時,當props的類型爲引用數據類型時,要注意在子組件中對對象進行深拷貝,防止隱性修改父組件的對象。
父組件中:
<!-- 父組件把改變數據的方法傳給child2,把數據傳給child,將數據通訊提高到父組件層次 --> <child2 :changeParent="changeMyself"></child2> <child :parent="parentChangeData"></child>複製代碼
child2中:
<!-- 子組件調用方法,將數據做爲參數 --> <button @click="changeParent(childData)">點我傳遞數據給父組件</button>複製代碼
export default {
// 子組件顯示聲明props父組件的函數
props: {
changeParent: {
requried: true, // 表明是否爲必傳
type: Function // 表明數據類型
}
},
data () {
return {
childData: '我要把數據傳給父'
}
}
}複製代碼
child中:
// 顯示聲明props父組件的數據
props: {
parent: {
requried: true, // 表明是否爲必傳
type: String, // 表明數據類型
default: '我是默認值'
}
}複製代碼
把須要跨頁面傳遞的數據放到url後面,跳轉到另外頁面時直接獲取url字符串獲取想要的參數便可。
{
path: '/params/:id'
name: '',
component: Sample
}複製代碼
<router-link :to="params/12">跳轉路由</router-link>複製代碼
在跳轉後的組件中用$route.params.id去獲取到這個id參數爲12,但這種只適合傳遞比較小的數據,數字之類的。
首先建立一箇中央時間總線,在須要使用的組件裏引入。組件A用this.Bus.$emit('eventName', value)
觸發事件,組件B用this.Bus.$on('eventName', value => { this.print(value) })
接收事件。在$emit
以前,必須已經$on
, 所以廣泛採用在created鉤子中進行$on
.
// 新建Bus.js文件
import Vue from 'vue'
export default new Vue()複製代碼
組件A傳送數據:
<script>
// 在須要使用的組件中引用
import Bus from '@/components/Bus'
export default {
data () {
return {
childData: '我是兄弟A,把個人數據放進eventBus'
}
},
methods: {
submit () {
// 觸發事件
Bus.$emit('change', this.childData)
}
}
}
</script>複製代碼
組件B接收數據:
get () {
// 監聽接收事件
Bus.$on('change', value => {
this.myData = value
})
}複製代碼
// 組件銷燬時,解除綁定
destroyed () {
Bus.$off('change')
}複製代碼
組件間的通訊均可以用EventBus實現, 不管有多少個組件,只要保證eventName不同就能夠了,專門設置一個空的Bus實例來做爲中央事件總線,而不是直接訪問root,更清晰也更利於管理。
傳統的eventBus只負責$emit和$on,與數據沒有任何的交集。這使得數據不是「長效」的,只在$emit後生效,而且同一組件屢次生成會屢次$on,路由切換時,還要考慮新組件的綁定和舊組件的解除綁定。
方法:考慮將$on放在Bus中完成,修改Bus爲:
// 新建Bus.js文件
// Bus進行監聽,將數據直接放在Bus中
import Vue from 'vue'
const Bus = new Vue({
data () {
return {
child7Val: ''
}
},
created () {
this.$on('change', value => {
this.child7Val = value
})
}
})
export default Bus複製代碼
發出數據的組件不變
<script>
// 在須要使用的組件中引用
import Bus from '@/components/Bus'
export default {
data () {
return {
childData: '我是兄弟A,把個人數據放進eventBus'
}
},
methods: {
submit () {
// 觸發事件
Bus.$emit('change', this.childData)
}
}
}
</script>複製代碼
接收數據的組件修改成用計算屬性直接從Bus中存取數據,使用計算屬性是爲了保證數據的動態性。
computed: {
child7Val () {
return Bus.child7Val
}
}複製代碼
個人理解是,Vuex至關於一個大型的專門用來儲存共享變量的倉庫。
在安裝Vuex以後,建立store.js文件
import Vue from 'vue'
import Vuex from 'vuex'
import app from './modules/app'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
// 存放共享變量
msg: '我是原始數據'
},
getter: {
// 至關於store中的計算屬性
mymsg: state => {
return state.msg
}
},
mutations: {
// 修改state,vue推薦使用大寫
MUTATIONSMSG (state, payload) {
state.msg = payload.msg
}
},
actions: {
// 與mutations相似,支持異步
mutationsMsg (context, payload) {
context.commit('MUTATIONSMSG', payload)
}
},
modules: {
app
},
strict: process.env.NODE_ENV !== 'production'
})
export default store複製代碼
當使用mutations來修改state時:
組件A使用store中的數據:
<template> <div class="child"> <h3>組件A</h3> {{$store.state.msg}} </div> </template>複製代碼
組件B修改store中的數據:
<script>
export default {
data () {
return {
myData: '組件B的數據'
}
},
methods: {
get () {
this.$store.commit('MUTATIONSMSG', this.myData)
}
}
}
</script>複製代碼
當使用actions修改state時:
<script>
export default {
data () {
return {
myData: '組件B的數據'
}
},
methods: {
get () {
this.$store.dispatch('mutationsMsg', this.myData)
}
}
}
</script>複製代碼
state用來存放共享變量,經過this.$store.state.[變量]來獲取共享變量的值。
getter,能夠增長一個getter派生狀態,(至關於store中的計算屬性),store.getters.方法名()用來得到共享變量的值。
mutations用來存放修改state的方法(至關於set)。
actions也是用來存放修改state的方法,不過action是在mutations的基礎上進行。在actions先commit mutations裏的方法,再由使用actions的組件進行dispatch
組件化的思想就是但願組件的獨立性,數據能互不干擾
經過組件A直接去修改組件B的值,好比雙向綁定,雖然方便,但增長了組件間的耦合性。最好就是若是組件A要修改組件B的值,那麼就將修改的數據暴露出去,由B獲得數據後,自行修改。