问题背景

近期做项目时需要父组件在created()钩子函数中异步获取数据然后传给子组件,子组件在created()钩子函数中获取传过来的props,并进行初始化,发现子组件在created()钩子函数中获取不到props。

问题原因

首先解释一下Vue的生命周期函数,以及组件嵌套时父子组件生命周期的触发顺序。
大家都知道,在单个组件加载的过程中生命周期的发生顺序是这样的:
vue官网生命周期图示
关于生命周期钩子的详情,这里就不解释了,不太了解的同学可以看一下这篇文章:详解vue生命周期

那么多个组件嵌套式,各组件的生命周期又是以怎样的顺序执行的呢?下面以一个简单地demo为例探究一下:

// ParentView.vue
<template>
  <div>
    <son-view></son-view>
  </div>
</template>

<script>
import SonView from './SonView.vue';

export default {
  name: 'parent-view',
  components: {
    [SonView.name]: SonView
  },
  data () {
    return {
      initData: {
        name: '123'
      },
    }
  },
  beforeCreate () {
    console.log('parent beforeCreate')
  },
  created () {
    console.log('parent created')
  },
  beforeMount () {
    console.log('parent beforeMount')
  },
  mounted () {
    console.log('parent mounted')
  }
}
</script>
// SonView.vue
<template>
  <div>123</div>
</template>

<script>
export default {
  name: 'son-view',
  props: ['data'],
  data () {
    return {

    }
  },
  beforeCreate () {
    console.log('son beforeCreate')
  },
  created () {
    console.log('son created')
  },
  beforeMount () {
    console.log('son beforeMount')
  },
  mounted () {
    console.log('son mounted')
  }
}
</script>

运行结果如下:
在这里插入图片描述
可见父子组件的生命周期执行顺序是父组件的beforeCreate=>父组件的created=>父组件的beforeMount=>子组件的beforeCreate=>子组件的created=>子组件的beforeMount=>子组件的mounted=>父组件的mounted

在父组件的created生命周期中异步获取数据并传递给子组件

// parent.vue
async created () {
    console.log(this.initData.city);
    let result = await this.$store.dispatch('customer/setting');
    this.initData = result.defaultArea;
    console.log(this.initData.city);
  },
// son.vue
created () {
    console.log(this.data.city);
  },

运行结果如下
在这里插入图片描述
如图可见在子组件运行created钩子函数时,父组件异步获取的数据还没有拿到返回值,而这个时候在子组件渲染时没有数据可能就会报错,解决的办法就两种。

解决方法

1.在子组件使用父组件的dom上加上判断条件v-if="data.city",这样该dom就会在拿到数据后再进行渲染,所以也就不会报错了。但是大部分情况下子组件不直接使用父组件传过来的数组而是使用处理后的数据,所以建议使用第二种方法。

2.在子组件加上判断条件的基础上对传过来的props进行监听,这样在拿到数据时就能第一时间监听到,并且进行赋值。

watch: {
    data () {
      console.log(this.data.city);
      this.init();
    }
  },
  methods: {
    init() {
      this.city = this.data.city;
    }
  }

运行结果如下:
在这里插入图片描述
这样这个问题就优雅的解决了。

最后补充一点知识,加上导航钩子后的生命周期执行顺序是这样的:全局导航守卫beforeEach=>父组件的beoreRouterEnter=>全局导航守卫afterEach=>父组件created=>子组件created=>子组件mounted=>父组件mounted=>父组件的beforeRouterEnternext

Logo

前往低代码交流专区

更多推荐