今天碰到vue開發父子組件通訊的一個小坑,狀況是這樣的:子組件使用echart展現圖表,所需options由父組件經過prop傳入,父組件中的options初始值爲空,在mounted鉤子函數中發起http請求獲取數據而後更新options,結果子組件沒法正確顯示圖表,通過一番查找解決了問題,經過本文作個記錄,也但願能幫到之後遇到相同問題的小夥伴,示例採用demo演示vue
<template>
<div id="app">
<child :message="message"></child>
</div>
</template>
<script>
import child from "./components/child";
export default {
name: "app",
components: {child },
data() {
return {
message:{}
};
},
mounted(){
// 模擬異步請求
setTimeout( () => this.message.age = 18,2000)
}
};
</script>
複製代碼
<template>
<div>{{age}}</div>
</template>
<script type='text/ecmascript-6'>
export default {
props: ["message"],
mounted(){
// 模擬echart的初始化操做
this.age = this.message.age
},
data() {
return {
age: null
};
}
};
</script>
複製代碼
{{message.age}}
是能夠顯示出18的,可是項目中是傳的options,子組件有個echart的setOptions操做,並非直接將prop的數據展現,因此demo寫成這樣。這裏是能夠分析出問題所在的,子組件mounted時父組件傳來的值
message
爲空對象,
this.message.age
就是underfined,因此頁面顯示爲空,2秒後父組件的值更新,子組件能夠拿到新的值,但mounted鉤子函數再也不觸發,因此
age
仍爲underfined,在網上查了下,一般的解決辦法是使用
watch
來監聽prop。改進後代碼以下
<template>
<div>{{age}}</div>
</template>
<script type='text/ecmascript-6'>
export default {
props: ["message"],
mounted() {
this.age = this.message.age;
},
watch: {
message(nv,ov){
this.age = nv.age
}
},
data() {
return {
age: null
};
}
};
</script>
複製代碼
看到網上的教程基本到這就結束了,子組件均可以正確渲染,but...我這頁面仍是老樣子,依舊一片空白,查看下調試工具,仍是這樣:bash
我擦,這是什麼鬼,debug一下發現watch裏的代碼根本不會執行,可message明明變了啊,因而又在網絡中遨遊了一番,而後看到了這麼一段話。 網絡
Vue在實例化的時候會給data中的每一個屬性加上getter setter
以實現響應式更新,可是新增的屬性沒法實現響應式更新,這裏咱們的父組件中message初始值爲
{}
,2秒中後設置他的age爲18,由於age是新增的屬性,實例初始化的時候並無給age加上getter和setter因此watch失敗。這裏盜一張官網的圖
修改代碼,在父組件中message的初始值中添加age
<template>
<div id="app">
<child :message="message"></child>
</div>
</template>
<script>
import child from "./components/child";
export default {
name: "app",
components: {child },
data() {
return {
message:{age:null}
};
},
mounted(){
// 模擬異步請求
setTimeout( () => this.message.age = 18,2000)
}
};
</script>
複製代碼
嗯,應該沒問題了吧,打開頁面,額。。。仍是熟悉的頁面app
有點小崩潰,冷靜,再分析一波。子組件watch了message,可是咱們在異步代碼中執行了this.message.age = 18
而不是相似
this.message = xxx
這種操做,雖然message這個對象確實發生了改變,可是卻沒法觸發setter,watch也就不起做用,查了下官網,發現了這個配置:
修改代碼以下:
<template>
<div>{{age}}</div>
</template>
<script type='text/ecmascript-6'>
export default {
props: ["message"],
mounted() {
this.age = this.message.age;
},
watch: {
message: {
deep: true,
handler(nv, ov) {
this.age = nv.age;
}
}
},
data() {
return {
age: null
};
}
};
</script>
複製代碼
再次打開頁面ecmascript
ok,完結撒花!!!