今天碰到vue开发父子组件通信的一个小坑,情况是这样的:子组件使用echart展示图表,所需options由父组件通过prop传入,父组件中的options初始值为空,在mounted钩子函数中发起http请求获取数据然后更新options,结果子组件无法正确显示图表,经过一番查找解决了问题,通过本文做个记录,也希望能帮到以后遇到相同问题的小伙伴,示例采用demo演示
父组件
<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>
复制代码
实际效果如图
其实这里子组件是拿到了更新后的值,如果template中的是{{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...我这页面还是老样子,依旧一片空白,查看下调试工具,还是这样:
我擦,这是什么鬼,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>
复制代码
嗯,应该没问题了吧,打开页面,额。。。还是熟悉的页面
有点小崩溃,冷静,再分析一波。子组件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>
复制代码
再次打开页面
ok,完结撒花!!!
所有评论(0)