组件懒加载导致的生命周期钩子触发顺序变化

有两个几乎一模一样的组件,然而在加载的时候却产生了不同的效果。排查了个把小时才发现是组件懒加载导致的,记录一下防止下次遇见又愣神。

先从简单开始讲起,有两个页面,其中 parent1 没用懒加载,parent2 加载子组件使用了懒加载。

// parent1
<template>
  <SyncChild />
</template>
<script>
import SyncChild from 'xxx'
export default {
  name: 'SyncParent',
  components: { SyncChild },
  mounted() {
    console.log('parent1 mounted')
  },
}
</script>

// SyncChild
<template>
  xxx
</template>
<script>
export default {
  name: 'SyncChild',
  mounted() {
    console.log('sync child mounted')
  },
}
</script>

// 页面刷新log顺序如下
// sync child mounted
// parent1 mounted

正常页面生命周期钩子触发的顺序是: 父组件beforeCreated -> 父组件created -> 子组件beforeCreated -> 子组件created -> 子组件mounted -> 父组件mounted,同步组件的加载子组件的mounted先于父组件。

// parent2
<template>
  <AsyncChild />
</template>
<script>
export default {
  components: {
    AsyncChild: () => import('xxx')
  },
  mounted() {
    console.log('parent2 mounted')
  },
}
</script>

// AsyncChild
<template>
  xxx
</template>
<script>
export default {
  name: 'AsyncChild',
  mounted() {
    console.log('async child mounted')
  },
}
</script>

// 页面刷新log顺序如下
// parent2 mounted
// async child mounted

页面首次加载时,由于使用了子组件懒加载,导致父组件渲染时子组件还未加载完。所以此时页面生命周期钩子触发的顺序是:父组件beforeCreated -> 父组件created -> 父组件mounted -> 异步子组件beforeCreated -> 异步子组件created -> 异步子组件mounted,异步子组件都是在父组件加载完后才开始加载的。如果页面已经加载过了,因为内存已经缓存子组件,所以此时异步子组件就变成了同步子组件,再次路由跳转到 parent2 的时候生命周期就和 parent1 逻辑一样了。

如果加入了 keep-alive 会是怎么样呢?

假如我在app.vue中使用了keep-alive组件:

<keep-alive>
  <router-view />
</keep-alive>

此时给同步组件分别加上 activated 生命周期钩子:

// parent1
<template>
  <SyncChild />
</template>
<script>
import SyncChild from 'xxx'
export default {
  components: { SyncChild },
  mounted() {
    console.log('parent1 mounted')
  },
  activated() {
    console.log('parent1 activated')
  },
}
</script>

// SyncChild
<template>
  xxx
</template>
<script>
export default {
  name: 'SyncChild',
  mounted() {
    console.log('sync child mounted')
  },
  activated() {
    console.log('sync child activated')
  },
}
</script>

// 页面刷新log顺序如下
// sync child mounted
// parent1 mounted
// sync child activated
// parent1 activated

此时父子组件的生命周期都正常触发了,且子组件在前父组件在后。

异步组件加上 activated 生命周期钩子:

// parent2
<template>
  <AsyncChild />
</template>
<script>
export default {
  components: {
    AsyncChild: () => import('xxx')
  },
  mounted() {
    console.log('parent2 mounted')
  },
  activated() {
    console.log('parent2 activated')
  },
}
</script>

// AsyncChild
<template>
  xxx
</template>
<script>
export default {
  name: 'AsyncChild',
  mounted() {
    console.log('async child mounted')
  },
  activated() {
    console.log('async child activated')
  },
}
</script>

// 页面刷新log顺序如下
// parent2 mounted
// parent2 activated
// async child mounted

此时父组件钩子先触发,然后才是子组件。而且子组件的 activated 钩子并没有触发。

首次加载时异步子组件的 mounted 会最后触发,因为异步组件需要下载。

异步子组件的 activated 并没有触发,这是因为子组件加载时父组件已经加载完成,此时并不知道是否是页面初次加载(因为子组件也可能是 v-if 等加载的)。没仔细看源码大概是在页面加载时会把 activated 等钩子收集起来待时触发,而收集钩子的时候异步子组件还没有下载完所以没收集到吧。

页面加载过后,再次路由跳转时异步子组件的 activated 触发顺序就和同步的没区别了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值