【Vue】生命周期

249 阅读9分钟

前言

开始正文之前,我们先回想以下几个问题:

  • 生命周期是什么?
  • Vue的生命周期有哪些?
  • 不同的周期又是什么含义
  • 它们的执行顺序是怎样的?

一、生命周期

1.1 生命周期是什么?

生命周期:Life Cycle。其基本涵义可以通俗地理解为“从摇篮到坟墓”(Cradle-to-Grave)的整个过程

在Vue中实例从创建到销毁的过程就是生命周期,即指从创建、初始化数据、编译模板、挂载DOM->渲染、更新->渲染、卸载等一系列过程我们可以把组件比喻成工厂里面的一条流水线,每个工人(生命周期)站在各自的岗位,当任务流转到工人身边的时候,工人就开始工作。

1.2 生命周期的作用是什么?

作用:让开发者有机会在特定阶段运行自己的代码

二、Vue的生命周期

2.1 Vue生命周期

Vue生命周期可划分的阶段:

  • 创建前后

    • beforeCreate: 组件实例被创建之初

      执行时组件实例还未创建,通常用于插件开发中执行一些初始化任务

    • created: 组件实例已经完全创建

      组件初始化完毕,各种数据可以使用,常用于异步数据获取

  • 挂载前后

    • beforeMount:组件挂载之前

      未执行渲染、更新,DOM未创建,已经完成了data和el数据初始化,但是页面中的内容还是Vue中的占位符,data中的message信息没有被挂载到BOM节点中,在这里可以在渲染前最后一次更改数据的机会,不会触发其它钩子函数

    • mounted:组件挂载到实例上去之后

      初始化结束,DOM已创建,可用于访问数据和DOM元素

  • 更新前后

    • beforeUpdate:data数据发生变化,更新之前。同时,我们需要知道,不是所有data数据发生变化都会触发该钩子函数的。因为beforedata 是针对视图层的数据

      更新前,可以用于获取更新前各种状态

    • updated:data数据更新之后

      更新后,所有状态已是最新

  • 销毁前后(关闭页面或关闭浏览器)

    • beforeDestory:组件实例销毁之前,此时我们仍然可以使用data与method方法

      销毁前,可用于一些定时或订阅的取消

    • destoryed:组件实例销毁之后,此时组件的data和method都已经被销毁

      组件已销毁,可用于一些定时器或监听的取消

  • 特殊场景

    • activated: keep-alive 缓存的组件激活时
    • deactivated:keep-alive 缓存的组件停用时
    • errorCaptured:捕获来自子孙组件的错误时被调用
      • 收到的参数
        1. 错误对象
        2. 发生错误的组件实例
        3. 说明错误来源类型的信息字符串
      • 错误的来源有
        1. 组件渲染
        2. 事件处理器
        3. 生命周期钩子
        4. setup() 函数
        5. 侦听器
        6. 自定义指令钩子
        7. 过渡钩子
      • 使用场景
        1. errorCaptured 中更改组件状态为用户显示一个错误状态(注意不要让错误状态再次渲染导致本次错误的内容,否则组件会陷入无限循环)
        2. errorCaptured 中可以通过返回false来阻止错误继续向上传递
      • 错误传递规则:
        1. 默认情况下,所有的错误都会被发送到应用级的 app.config.errorHandler (前提是这个函数已经定义),这样这些错误都能在一个统一的地方报告给分析服务
        2. 如果组件的继承链或者组件链上存在多个 errorCaptured 钩子,对于同一个错误,这些钩子会被按从底至上的顺序--调用。这个过程被称为“向上传递”,类似于原生DOM事件的冒泡机制
        3. 如果errorCaptured钩子本身抛出一个错误,那么这个错误和原生捕获到的错误都将被发送到app.config.errorHandler
        4. errorCaptured 钩子可以通过返回 false 来阻止错误继续向上传递。即表示“这个错误已经被处理了,应该被忽略”,它将阻止其它的 errorCaptured 钩子或 app.config.errorHandler因这个错误被调用

2.2 生命周期图示

lifecycle.16e4c08e.png

  • beforeCreate -> created:初始化 vue 实例,进行数据检测
  • created:
    • 完成数据观测,属性与方法的运算,watch、event 事件回调的配置
    • 可调用 methods 中的方法,访问和修改 data 数据触发响应式渲染 dom,可通过 computed 和 watch 完成数据计算
    • 此时 vm.$el 并没有被创建
  • created -> beforeMount
    • 判断是否存在 el 选项,若不存在则停止编译,直到调用 vm.$mount(el)才会继续编译
    • 优先级:render > template > outerHTML
    • vm.el获取到的是挂载 DOM 的
  • beforeMount
    • 在此阶段可获取到vm.el
    • 此阶段vm.el虽已完成DOM初始化,但并未挂载在el选项上
  • beforeMount -> mounted
    • 此阶段vm.el完成挂载,vm.$el生成的DOM替换了el选项所对应的DOM
  • mounted
    • vm.el已完成DOM的挂载与渲染,此刻打印vm.$el,发现之前的挂载点及内容已被替换成新的DOM
  • beforeUpdate
    • 更新的数据必须是被渲染在模板上的(el、template、render之一)
    • 此时view层还未更新
    • 若在beforeUpdate中再次修改数据,不会再次触发更新方法
  • updated
    • 完成view层的更新
    • 若在updated中再次修改数据,会再次触发更新方法(beforeUpdate、updated)
  • beforeDestroy
    • 实例被销毁前调用,此时实例属性与方法仍可访问
  • destroyed
    • 完全销毁一个实例。可清理它与其它实例的连接,解绑它的全部指令及事件监听器
    • 并不能清除DOM,仅仅销毁实例

2.3 生命周期详细图解

3723c888d3524c399d3f90c54009efd5~tplv-k3u1fbpfcp-zoom-in-crop-mark_1512_0_0_0.webp

<script>
export default {  
  // 在实例初始化之后,数据观测和事件配置之前被调用
  beforeCreate(){
    console.log('beforeCreate----创建前');
  },
  // 实例已经创建完成之后被调用
  created(){
    console.log('created----创建之后');
  },
  // 页面准备挂载时候被调用,此时相关的渲染函数首次被调用
  beforeMount(){
    console.log('beforeMount----挂载开始');
  },
  // 挂在完成,也就是模板中的HTML渲染到HTML页面中,此时一般可以做一些ajax操作,mounted只会执行一次。
  mounted(){
    console.log('mounted----挂载完成');
  },
  // 数据更新之前被调用
  beforeUpdate(){
    console.log('beforeUpdate----更新之前被调用');
  },
  //数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子
  updated(){
    console.log('updated----更新后');
  },
  // 我们将要销毁整个页面或实例时调用
  beforeDestroy(){
    console.log('beforeDestroy----销毁前');
  },
  // 我们的整个页面或实例被销毁之后调用
  destroyed(){
    console.log('destroyed----销毁后');
  },
  // 被 keep-alive 缓存的组件激活时调用
  activated(){
    console.log('activated');
  },
  //  deactivated配合keep-alive来使用
  //  使用了keep-alive就不会调用beforeDestory和destoryed钩子了
  //  因为组件没有被销毁,而是被缓存起来了
  //  所以deactivated钩子可以看做是beforeDestory和destoryed的替代
  deactivated(){
    console.log('deactivated');
  }
}
</script>

三、Vue生命周期知多少

3.1 vue生命周期钩子函数为什么不能使用箭头函数?

Vue生命周期钩子会自动绑定this上下文到实例中,因此我们可以访问数据,对property和方法进行运算。这意味着我们不能使用箭头函数来定义一个生命周期方法,因为箭头函数绑定了父级上下文,所以 this 不会指向预期的组件实例。

3.2 数据请求在created和mouted的区别?

区别:

  • 执行时机:createmounted的更早
  • DOM节点:
    • create 是在组件实例一旦创建完成的时候立刻调用,这时页面 DOM 节点尚未生成;
    • mounted是在页面 DOM 节点渲染完毕后就立刻执行的,这时页面 DOM 节点已生成

相同点:

  • 都能拿到实例对象的属性和方法

总结:

请求放在mounted中可能导致页面白屏闪动的情况,因为此时页面DOM结构已经生成。因此建议将对无依赖的请求放到create

3.3 Vue中,有哪两个钩子会执行多次

beforeUpdate 和 update。因为只要data中的数据发生变化,就会触发这两个钩子执行

3.4 Vue中,哪些钩子只会执行1次?

beforeCreate、created和beforeMount、mounted

3.5 完整的父子组件生命周期

  • 渲染过程

    父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted

  • 子组件更新过程

    • 影响到父组件:父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated
    • 不影响父组件:子beforeUpdate ->子updated
  • 父组件更新过程

    • 影响到子组件:父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated
    • 不影响到子组件:父beforeUpdate -> 父updated
  • 销毁过程

    父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed

总结:父组件等待子组件完成后,才会执行自己对应完成的钩子

父子组件生命周期完整的执行顺序图:

image.png

3.6 Vue2 和 Vue3 的生命周期有什么区别?

  1. Vue3 的生命周期函数在使用前需要引入
  2. Vue3 中,setup(),它是在beforeCreatecreated之前运行的,所以你可以用它来代替这两个钩子
  3. Vue3 相比原有的生命周期,基本都是存在的,Vue3只不过有了新的命名:beforeDestroy 变成了beforeUnmounteddestroy 变成了 unmounted,名字变了但是原理还是没变的;
  4. 新增了生命周期函数onRenderTrackedonRenderTriggered
    • onRenderTracked:调试钩子,每次触发页面重新渲染时自动执行。

      状态跟踪,他会跟踪页面上所有响应式变量和方法的状态,也就是我们return出去的值,只要页面有update的情况,他就会跟踪,然后生成一个event对象,我们可以通过event对象来查找程序所存在的问题。

      特别注意,这个钩子仅在开发模式下可用,且在服务器端渲染期间不会被调用

    • onRenderTriggered:调试钩子,当响应式依赖的变更触发了组件渲染时调用。

      状态触发,不会跟踪每一个值,而是给你变化值的信息,并且新值和旧值都会明确的展示出来。

      如果把onRenderTracked是每个值都追踪,而onRenderTriggered是精准追踪,进行针对性调试

      特别注意,这个钩子仅在开发模式下可用,且在服务器端渲染期间不会被调用

资料来源