likes
comments
collection
share

背了那么多八股文,也要会在工作中处理父子组件传值问题呀!

作者站长头像
站长
· 阅读数 34

上一篇更文看到大家对工作做遇到的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的做法

大家有什么更好的方法欢迎在评论区进行讨论,希望不吝赐教

往期好文--优雅封装--我用组件化把上千行代码优化到15行

转载自:https://juejin.cn/post/7163455746556821541
评论
请登录