本文详细解释Vue2及Vue3的生命周期,并在最后列出了关于Vue生命周期常见的面试题,便于各位小伙伴快速学习相关内容!
一、vue2的生命周期
- beforeCreate
在实例初始化之后,进行数据侦听和事件/侦听器的配置之前同步调用
详解:
- 获取不到数据属性(此时 data、props、computed、methods 等都还未初始化),尝试访问 this.xxx 会返回 undefined
- 模板尚未编译,真实DOM不存在,访问this.$el为undefined
- 事件系统尚未初始化,无法使用this.$on、emit等方法
- created
在实例创建完成之后被立即同步调用。在这一步中,实例已完成对选项的处理,意味着以下内容已被配置完毕:数据侦听、计算属性、方法、事件/侦听器的回调函数。然而,挂载阶段还没开始,且$el.property目前暂不可用
详解:
- 可访问和修改数据,data、props、computed、methods 等都已配置完成
- DOM还未挂载,this.$el 仍为 undefined
- 事件系统初始化完毕,可使用this.$on、emit等方法
- 可用于调用接口初始化数据
$el.property 中的 el指的是Vue实例的根DOM元素(即挂载的DOM节点,一般为id为app的那个
元素),而.proterty是一个泛指的占位符,表示访问的任意DOM属性和方法(mounted
生命周期之后可用)
- beforeMount
在实例挂载到 DOM 之前触发,此时 Vue 已经完成了模板(template)的编译(将模板转换为渲染函数render),但尚未将生成的虚拟 DOM 替换到页面上变成真实DOM
render函数的作用是生成虚拟DOM,无论是通过模板(
template
)还是直接手写的render
函数,最终都会被 Vue 转换为一个render
函数
详解:
- 与created的区别就是到达beforeMount钩子时模板已经编译完成,存在虚拟DOM
- 此时this.$el访问的是原始元素(区分于实例的元素),不是真实DOM
- mounted
实例被挂载之后调用,标志着组件已经完成了初始渲染,真实 DOM 已经创建并插入到文档中。但是 mounted 不会保证所有的子组件也都被挂载完成,可以使用nextTick邓达斯渲染完毕再执行相关操作
详解:
- DOM 完全可用,使用this.$el可以得到实例上真实的dom
- 计算属性已经完成首次计算,所有初始数据都已经反映在视图上了
- 但子组件的mounted可能尚未执行,可以使用nextTick等待整个视图渲染完毕
- beforeUpdate(防抖节流)
在数据变化后、DOM 更新前被调用,为开发者提供了一个干预更新过程的机会
详解:
- 不会在初始化时执行,仅在数据变化导致重新渲染时触发
- 数据已经是新值,计算属性已经重新计算、但是DOM为旧的
- 适合临时添加事件监听器、保存当前的DOM状态(如滚动位置)
- 不应该执行耗时操作,容易阻塞渲染
- 多DOM更新操作考虑使用requestAnimationFrame进行视觉更新
- updated(防抖节流)
在数据更改导致的虚拟 DOM 重新渲染和更新完毕之后被调用。
详解:
- 在此钩子中修改数据可能导致无限更新循环,应该避免状态修改
- 如需要响应数据变化,应使用计算属性或watcher
- 常用于清除定时器、取消事件监听、清理第三方库实例
- beforeDestroy
组件即将被销毁,但所有功能仍完整可用
详解:
- 用于清理组件专用资源、取消副作用操作、执行销毁前的状态保存
- vue不会自动清理监听器和第三方库实例,未清理的资源可能导致内存泄漏
- destroyed
实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁
详解:
- 日志记录
- 通知外部系统组件以及销毁
- 清理不依赖实例的状态
二、vue3的生命周期
1、写法
- beforeMount —> onBeforeMount
- mounted —> onMounted
- beforeUpdate —> onBeforeUpdate
- updated —> onUpadate
- beforeDetory —> onBeforeUnmount
- detory —> onUnmounted
2、setup替代 —> beforeCreate和created
- setup函数会在beforeCreate之前执行,此时组件尚未初始化(数据/方法未生成)
- 但是原来写在beforeCreate和created的逻辑可以写在setup中
- 用于定义响应式数据、声明方法、组合逻辑
三、vue初始化过程
-
初始化阶段
初始化生命周期相关属性、事件系统、渲染函数相关属性
- 对应beforeCreate:实例刚初始化,无法访问到data、computed、methods
-
数据观测阶段
初始化data、props、methods、computed、watch等属性
- vue会遍历data对象上的属性,使用Object.defineProperty(vue2)或Proxy(vue3)将它们转换为getter/setter
- 当组件渲染时,访问数据属性触发getter,将当前Watcher(依赖)收集到熟悉的依赖列表中
- 当数据变化时,setter会通知所以依赖进行更新
- 对应created钩子:已完成数据观测、可访问data、computed、methods,但尚未挂载DOM,$el不可用
- vue会遍历data对象上的属性,使用Object.defineProperty(vue2)或Proxy(vue3)将它们转换为getter/setter
-
模板编译阶段
vue将模板(template)编译为渲染函数(render),编译过程会将v-if\v-for等语法转换为JS代码
- 检查是否有template选项,有则编译为render函数,无则将el的outerHTML作为模板编译
- 将template编译为抽象语法树(AST)
- 优化AST(标记出静态节点:静态节点再后续变更会被跳过)
- 生成render
-
挂载阶段
- 创建组件实例的渲染Watcher
- 执行beforeMount:模板编译完成,即将首次渲染
- 执行render,生成虚拟DOM,与旧DOM进行比较,计算出最小变更,应用变更到真实DOM上
- 创建真实DOM并替换el(或插入el内部)
- 执行mounted:实例已经挂载到DOM,可访问$el,但不保证所有组件都已经渲染
-
更新阶段
当响应式数据变化时,触发渲染Watcher更新
- 数据变化,DOM更新之前执行beforeUpdate
- DOM更新完成之后执行updated:在这个钩子不要修改状态,否则会无限循环
-
卸载阶段
- 在实例销毁之前会执行beforeUnmount(vue2是beforedestory),此时实例完全可用
- 实例销毁之后,所有绑定移除,执行unmounted/destoryed
四、生命周期面试题
1、生命周期钩子在什么时候调用?
答:
- beforeCreate在实例初始化之后,数据观测之前调用
- created在实例创建完成,即数据观测也已经完成,属性、事件和方法可以访问,但无dom
- beforeMounted在实例挂载之前调用,render函数首次调用完成,存在虚拟DOM
- mounted在实例挂载完毕后调用,可访问属性方法,虚拟DOM已经转换为真实DOM
- beforeUpdate在数据更新、但DOM还未更新时调用
- updated在数据更新导致的DOM重新渲染之后调用
- beforeDestory在实例销毁之前调用,此时实例还存在
- Destory在实例销毁之后调用
2、生命周期钩子内部可以做什么事情?
- created 实例已经创建完成,数据完成观测,可以进行数据、资源的请求。
- mounted 实例已经挂载完成,拿到了vue实例的DOM,可以进行一些DOM操作。
- beforeUpdate 可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。
- updated 可执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态, 因为这可能会导致更新无限循环。 该钩子在服务器端渲染期间不被调用。
- beforeDestroy 可以在组件销毁之前进行一些优化操作,比如关闭定时器,关闭监听,用来销毁一些离开这个页面不愿理还进行的操作。
3、created和mounted的区别?
答:
- created:实例已经创建完成,数据完成观测、但是DOM还未挂载
- mounted:DOM已经挂载,可以操作真实的DOM
4、在哪个请求发起AJAX请求最合适?
答:在created钩子发起请求最合适,因为
- 此时数据已经完成观测,可以设置响应式数据
- 比mounted更早执行,能更早获取数据
- 但是需要DOM信息的请求应该放在mounted+$nextTick中
- nextTick将回调延迟到下次DOM更新循环之后执行
5、第一次加载页面会触发那些钩子
答:前面四个钩子
6、简述父子组件的生命周期
总结起来就是三个大阶段都是子组件的最后一步由父组件完成
-
渲染阶段:
父组件 beforeCreate
父组件 created
父组件 beforeMount
开始编译父组件模板
子组件 beforeCreate
子组件 created
子组件 beforeMount
子组件 mounted (此时子组件DOM已挂载)
父组件 mounted (父组件DOM最终挂载完成)
-
更新阶段
父组件
beforeUpdate
子组件
beforeUpdate
子组件
updated
父组件
updated
-
销毁阶段
父组件
beforeDestroy
/beforeUnmount
子组件
beforeDestroy
/beforeUnmount
子组件
destroyed
/unmounted
父组件
destroyed
/unmounted