一、Vue.js 的生命周期
什么是生命周期?
Vue 组件从创建、更新到销毁,会经历一系列的过程,这些过程称为组件的生命周期。Vue 提供了多个生命周期钩子(hook),让我们在组件的不同阶段执行特定的逻辑。
主要的生命周期钩子:
beforeCreate
:实例初始化之后,数据观测和事件配置之前。created
:实例创建完成,属性已绑定,但 DOM 未生成,$el
还不可用。beforeMount
:在挂载开始之前被调用,相关的render
函数首次被调用。mounted
:实例挂载完成,el
被新创建的vm.$el
替换,并挂载到实例上,此时 DOM 可访问。beforeUpdate
:响应式数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。updated
:由于数据更改导致的虚拟 DOM 重新渲染和打补丁之后调用。beforeDestroy/
beforeUnmount:实例销毁之前调用。destroyed/
unmounted:实例销毁后调用。
二、什么是 nextTick
?
nextTick
的作用:
- 异步 DOM 更新机制:Vue 在数据变化后,不会立即同步更新 DOM,而是将其放入一个队列,在下一个事件循环(Tick)中执行 DOM 更新。这是为了优化性能,避免频繁的 DOM 操作。
nextTick
方法:提供一个回调函数,在下次 DOM 更新循环结束后执行。确保在数据变化导致的 DOM 更新完成后,再执行某些操作// vue3 的写法 nextTick(()=>{ //需异步执行的内容 }) // nextTick 基于微任务队列:Promise.resolve().then()、MutationObserver 等。 // 确保在 DOM 更新后执行回调:将回调函数放入微任务队列,等待 DOM 更新完成后立即执行
nextTick
的使用:
- 实例方法:
this.$nextTick(callback)
- 全局方法:
Vue.nextTick(callback)
三、
生命周期与 nextTick
的关系
生命周期钩子中的 DOM 状态
beforeCreate
阶段:
- 特点:数据和方法还未初始化,无法操作DOM节点。
created
阶段:
- 特点:实例已经创建,数据和方法已初始化,但未进行 DOM 渲染,
this.$el
还不可用。 - 注意:此时无法进行与 DOM 有关的操作。
mounted
阶段:
- 特点:组件已挂载,
this.$el
可用,初始渲染完成。 - 注意:可以访问和操作组件的 DOM 元素。
updated
阶段:
- 特点:组件数据更新后,DOM 重新渲染并打补丁,此时已完成 DOM 更新。
- 注意:可以获取更新后的 DOM 状态。
需要使用 nextTick
的场景
在数据更新后,立即想操作更新后的 DOM 元素:
- 问题:由于 DOM 更新是异步的,数据更新后,DOM 还未更新,直接操作会得到旧的 DOM 状态。
- 解决:使用
this.$nextTick
,确保在 DOM 更新完成后再执行操作。
在生命周期钩子中需要等待 DOM 更新:
-
在
mounted
钩子中,初始 DOM 已渲染完成,可以直接操作 DOM,一般不需要nextTick
。mounted() { // 可以直接访问 DOM console.log(this.$refs.myElement.offsetHeight); }
-
在
updated
钩子中,DOM 已更新完毕,DOM 是最新的,也不需要nextTick
。updated() { // DOM 已更新,可以直接操作 console.log(this.$refs.myElement.offsetHeight); }
-
在beforeUpdate
钩子中,组件的数据已变化,但 DOM 还未更新,需使用nextTick。beforeUpdate() { // 数据已更新,DOM 还未更新 this.$nextTick(() => { // DOM 更新后执行 console.log(this.$refs.myElement.offsetHeight); }); }
-
在数据更新后,需要在下一次更新前执行操作,需要使用
nextTick
。//在某个方法中修改了组件的响应式数据,想要立即获取更新后的 DOM 状态 methods: { addItem() { this.list.push(newItem); // 立即尝试获取列表高度 console.log(this.$refs.listContainer.offsetHeight); // 可能得到旧的高度 } } //解决方案: methods: { addItem() { this.list.push(newItem); this.$nextTick(() => { // DOM 更新后获取列表高度 console.log(this.$refs.listContainer.offsetHeight); }); } }
四、总结
在生命周期钩子中:
created
:DOM 未生成,无法操作 DOM。mounted
:初始 DOM 已渲染,可直接操作 DOM,无需nextTick
。beforeUpdate
:数据已更新,DOM 未更新,如需操作更新后的 DOM,需使用nextTick
。updated
:DOM 已更新,可直接操作更新后的 DOM,无需nextTick
。
在数据更新的方法中:
- 数据变化后,DOM 更新是异步的。
- 如果需要在数据更新后立即获取更新后的 DOM,需要使用
this.$nextTick
。