背了那么多八股文,也要会在工作中处理父子组件传值问题呀!
上一篇更文看到大家对工作做遇到的bug以及解决方案那么感兴趣,收藏比点赞还多,那么就再更新一些日常工作中遇到的bug和一些解决的思路。
我需要父组件传入一个数据来对子组件的数据进行加工,但是在子组件的mounted生命周期中,却无法获取父组件传入的值,这是我遇到的一个bug,传入的值是动态通过接口获取
的,大概看到这屏幕前的你已经知道bug出现在什么地方了,那么接着看看吧
父子组件生命周期
我们尝试打印出父子组件的生命周期,其中dataSource
是通过getDataSource函数在接口中获取数据的,在父组件的mounted中调用。
//child.vue
beforeCreate(){
console.log('c-beforeCreate')
},
created(){
console.log('c-created',this.dataSource)
},
beforeMount(){
console.log('c-beforeMount',this.dataSource)
},
//father.vue
<component :dataSource="dataSource"><component>
beforeCreate(){
console.log('p-beforeCreate')
},
created(){
console.log('p-created')
},
beforeMount(){
console.log('p-beforeMount',this.dataSource)
},
mounted() {
this.getDataSource()//获取接口的数据赋值给dataSource
console.log('p-mounted',this.dataSource)
}
从我们打印出来的生命周期中可以看出来,子组件已经走完了自己的生命周期,而父组件的mouted是在子组件走完之后再调用的。
也就是说父组件mounted拿到的值要传给子组件的时候,子组件的mounted已经走完了,所以我们可以看到子组件的每个生命周期中我们都拿不到dataSource
的值
小tips
切换子组件,父组件不会触发生命周期,子组件会重新走一遍生命周期
遇到的问题
在下面这个实例中我们就可以看出会遇到什么问题
//father.vue
<component
:dataSource="dataSource"
></component>
mounted() {
let vm = this;
vm.getDatasource();//获取
}
methods:{
getDatasource(){
...
this.dataSource = res.data.data //给dataSource赋值
}
}
props: {
dataSource:Object,
},
mounted() {
this.fetchData();
},
fetchData(){
let vm = this
vm.paging.tableData.forEach((item) => {
//需要用到传入的数据对子组件内的数据进行加工
vm.dataSource[item.datasourceId] &&
//由于拿不到dataSource的值,datasourceName无法被set到tableData的数据上 所以一直会出现bug
vm.$set(
item,
"datasourceName",
vm.dataSource[item.datasourceId].cname
);
});
}
我在父组件中mouted定义的数据,要传到我们的子组件上,子组件刚好需要拿到这个数据在mouted中进行使用,这样的话在mouted中输出的数据是空的
因为子组件的mouted是比父组件的mounted更早执行的,所以以上的写法是无法拿到我们传入的值去进行数据加工的,这就是bug出现的原因,笔者在项目中遇到了这个问题,虽然平时关于我们生命周期的八股文背的挺多,但是真正遇到这个问题生命周期执行顺序的问题,即使你背的再好,我们需要也找方法来进行解决。
问题总结:
1.子组件mounted执行,获取应该传入的dataSource,数据无法加工
2.这时父组件的生命周期在beforeMounted,还未传入dataSource
3.父组件mounted获取到dataSource
4.父组件传dataSource给子组件
5.此时子组件的mounted已经执行完成,第一次加工无法实现
方案一:监听传入的数据 再进行加工
虽然我们无法在mouted的时候进行加工数据直接显示到页面上,但是我们可以通过监听传入的数据,当数据传入的时候,进行加工,在进行一次更行就好啦,具体方法也是比较简单,但是比较直观也比较笨的方法,直接watch我们传入的dataSource
//Child
watch:{
dataSource:{
handler(nv){
let vm = this
vm.updateData()//接受到dataSource在进行数据加工
}
}
},
methods:{
updateData(){
let vm = this
//更新我们的tableData数据
vm.paging.tableData.forEach((item) => {
vm.dataSource[item.datasourceId] &&
vm.$set(
item,
"datasourceName",
vm.dataSource[item.datasourceId].cname
);
});
}
}
当dataSource监听到具体值的时候,我们再去进行数据的加工,这样就能保证即使数据在父组件的mounted中传入,子组件也能接受到并且加工,这里的话我是把加工步骤从子组件的mounted中抽取出来了,监听到dataSource有值就直接进行加工。
方案二:强制更新子组件
这里更新的方式也比较多,大家有兴趣的话可以自己尝试一下,由于强制更新的效果
- 使用
v-if
- 使用内置的
forceUpdate
方法 - 利用
key
值的变化触发组件更新: 最佳方式
最后
有的小伙伴可能会觉得那么既然按照父子生命周期的执行顺序,在created中调用getSource不就好了吗?但是实际上即使你在created中调用了getsource,等到接口数据返回的时候,子组件的生命周期早就走完了,同样还是获取不到想要的传入值的,所以理想的方案还是参考watch的做法
大家有什么更好的方法欢迎在评论区进行讨论,希望不吝赐教
转载自:https://juejin.cn/post/7163455746556821541