当前位置: 首页 > news >正文

Vue框架之钩子函数详解

Vue框架之生命周期主要钩子函数详解

    • 一、Vue生命周期的整体流程
    • 二、创建阶段:初始化组件实例
      • 2.1 `beforeCreate`:实例创建前
      • 2.2 `created`:实例创建后
    • 三、挂载阶段:组件与DOM结合
      • 3.1 `beforeMount`:挂载前
      • 3.2 `mounted`:挂载后
    • 四、更新阶段:数据变化触发重渲染
      • 4.1 `beforeUpdate`:更新前
      • 4.2 `updated`:更新后
    • 五、销毁阶段:组件实例的清理
      • 5.1 `beforeDestroy`:销毁前
      • 5.2 `destroyed`:销毁后
    • 六、特殊场景的钩子函数
      • 6.1 keep-alive 相关钩子
      • 6.2 错误捕获钩子:`errorCaptured`
    • 七、生命周期钩子的执行顺序与实战示例
      • 7.1 单组件执行顺序
      • 7.2 父子组件执行顺序
    • 八、常见问题与避坑指南
      • 8.1 避免在`updated`中修改数据
      • 8.2 子组件`mounted`晚于父组件`mounted`
      • 8.3 清理资源是重中之重
    • 九、Vue 3中的生命周期变化

Vue的生命周期是指组件从创建到销毁的整个过程,而生命周期钩子函数则是在这个过程中特定时间点自动执行的函数。掌握这些钩子函数,能让我们在合适的时机执行特定操作(如数据请求、DOM操作、资源清理等),是Vue开发的核心基础。

一、Vue生命周期的整体流程

Vue组件的生命周期可分为4个阶段,每个阶段包含若干钩子函数,整体流程如下:

  1. 创建阶段:组件实例从初始化到挂载前的过程
  2. 挂载阶段:组件实例挂载到DOM的过程
  3. 更新阶段:组件数据变化导致重新渲染的过程
  4. 销毁阶段:组件实例从DOM中移除并清理资源的过程

二、创建阶段:初始化组件实例

创建阶段是组件实例从无到有的过程,主要完成数据观测(响应式处理)、事件初始化等工作,此时尚未涉及DOM操作。

2.1 beforeCreate:实例创建前

  • 执行时机:Vue实例初始化后(new Vue()之后),数据观测(dataprops)和事件机制初始化前调用。
  • 特点
    • 无法访问datapropsmethods中的数据和方法(此时尚未初始化)。
    • 不能进行DOM操作(DOM尚未生成)。
  • 适用场景:极少使用,可用于初始化非响应式数据(如临时变量)。
new Vue({data() {return { message: 'Hello' };},beforeCreate() {console.log('beforeCreate:', this.message); // undefined(无法访问data)console.log('methods:', this.getMsg); // undefined(无法访问methods)},methods: {getMsg() { return this.message; }}
});

2.2 created:实例创建后

  • 执行时机:Vue实例初始化完成后调用,此时已完成dataprops的响应式处理和methods的绑定,但尚未开始DOM编译(模板未挂载到DOM)。
  • 特点
    • 可访问datapropsmethods中的数据和方法。
    • 仍无法进行DOM操作($el属性不存在,DOM未生成)。
  • 适用场景
    • 发起初始化数据请求(如获取列表数据)。
    • 初始化数据(如对data中的数据进行预处理)。
    • 绑定自定义事件。
new Vue({el: '#app',data() {return { list: [] };},created() {console.log('created:', this.list); // [](可访问data)console.log('$el:', this.$el); // undefined(DOM未挂载)// 示例:发起数据请求axios.get('/api/list').then(response => {this.list = response.data; // 数据响应后更新list(响应式)});}
});

三、挂载阶段:组件与DOM结合

挂载阶段是组件实例与DOM关联的过程,核心是将编译后的模板挂载到页面中,此时开始具备DOM操作能力。

3.1 beforeMount:挂载前

  • 执行时机:模板编译(解析指令、插值表达式等)完成后,DOM挂载到页面之前调用。
  • 特点
    • 已完成模板编译,生成虚拟DOM(但未渲染到页面)。
    • $el属性存在(指向即将挂载的DOM元素),但内容仍为原始模板(未替换数据)。
    • 仍无法操作DOM(数据未渲染,操作无意义)。
  • 适用场景:极少使用,可用于获取模板编译前的DOM结构。
new Vue({el: '#app',template: '<div>{{ message }}</div>',data() { return { message: '挂载前' }; },beforeMount() {console.log('beforeMount $el:', this.$el); // <div>{{ message }}</div>(未替换)console.log('页面内容:', document.getElementById('app').innerHTML); // 原始模板}
});

3.2 mounted:挂载后

  • 执行时机:模板挂载到DOM后调用,此时页面已显示渲染后的内容。
  • 特点
    • DOM已完全渲染,可通过$el或原生DOM API(如document.getElementById)操作DOM。
    • 子组件的mounted可能在父组件的mounted之后执行(因渲染顺序)。
  • 适用场景
    • 执行DOM操作(如初始化第三方UI插件:图表、地图等,需依赖DOM元素)。
    • 监听DOM事件(如滚动、resize)。
    • 若数据请求依赖DOM尺寸(如根据容器宽度请求不同数据),可在此发起请求。
new Vue({el: '#app',data() { return { width: 0 }; },mounted() {// 获取DOM元素宽度this.width = this.$el.offsetWidth;console.log('挂载后宽度:', this.width);// 初始化第三方图表插件(假设页面有<div id="chart"></div>)this.chart = new Chart(document.getElementById('chart'), {type: 'line',data: { labels: ['1月', '2月'], datasets: [{ data: [10, 20] }] }});// 监听滚动事件window.addEventListener('scroll', this.handleScroll);},methods: {handleScroll() { /* 处理滚动逻辑 */ }}
});

四、更新阶段:数据变化触发重渲染

当组件的dataprops发生变化时,会进入更新阶段,触发重新渲染,此阶段的钩子函数用于监控或干预更新过程。

4.1 beforeUpdate:更新前

  • 执行时机:数据发生变化后,虚拟DOM重新渲染前调用。
  • 特点
    • 此时data中的数据已更新,但DOM尚未重新渲染(页面显示旧数据)。
    • 可获取更新前的DOM状态。
  • 适用场景:获取更新前的DOM信息(如滚动位置、输入框光标位置),用于更新后恢复状态。
new Vue({el: '#app',data() { return { count: 0 }; },template: '<div>{{ count }} <button @click="count++">+1</button></div>',beforeUpdate() {console.log('更新前data:', this.count); // 新值(如1)console.log('更新前DOM:', this.$el.textContent); // 旧值(如0)}
});

4.2 updated:更新后

  • 执行时机:虚拟DOM重新渲染并更新到页面后调用,此时页面显示最新数据。
  • 特点
    • data和DOM均已更新,可获取最新的DOM状态。
    • 若在updated中修改data,会再次触发更新(可能导致无限循环,需避免)。
  • 适用场景
    • 基于最新DOM状态执行操作(如根据新内容调整元素样式)。
    • 同步第三方插件数据(如图表数据更新后,重新绘制图表)。
new Vue({el: '#app',data() { return { data: [10, 20] }; },template: '<div>{{ data }}</div>',updated() {console.log('更新后DOM:', this.$el.textContent); // 显示最新data// 若图表数据依赖this.data,更新后重新绘制if (this.chart) {this.chart.data.datasets[0].data = this.data;this.chart.update();}}
});

五、销毁阶段:组件实例的清理

当组件被销毁(如v-if="false"移除组件、路由切换)时,进入销毁阶段,此阶段的钩子函数用于清理资源,避免内存泄漏。

5.1 beforeDestroy:销毁前

  • 执行时机:组件实例销毁前调用,此时组件仍处于正常工作状态。
  • 特点
    • 可访问datamethods、DOM等所有资源。
    • 子组件的beforeDestroy会在父组件的beforeDestroy前执行。
  • 适用场景
    • 清理定时器、事件监听器(避免组件销毁后仍执行)。
    • 取消未完成的请求(避免资源浪费)。
    • 解绑自定义事件。
new Vue({el: '#app',data() {return { timer: null };},mounted() {// 启动定时器this.timer = setInterval(() => {console.log('定时器执行中...');}, 1000);// 绑定事件window.addEventListener('resize', this.handleResize);},beforeDestroy() {// 清理定时器clearInterval(this.timer);// 移除事件监听window.removeEventListener('resize', this.handleResize);// 取消未完成的请求(假设使用axios)if (this.source) {this.source.cancel('组件销毁,取消请求');}},methods: {handleResize() { /* 处理窗口大小变化 */ }}
});

5.2 destroyed:销毁后

  • 执行时机:组件实例销毁后调用,此时组件的所有资源已被释放。
  • 特点
    • 组件的响应式数据、事件监听、子组件等均已被销毁。
    • 仍可访问$el,但DOM可能已被移除(取决于销毁方式)。
  • 适用场景:极少使用,可用于最终的资源清理或日志记录。
new Vue({destroyed() {console.log('组件已销毁');// 记录销毁日志console.log('组件销毁时间:', new Date().toLocaleString());}
});

六、特殊场景的钩子函数

除上述核心钩子外,Vue还提供了针对特殊场景的钩子函数:

6.1 keep-alive 相关钩子

keep-alive用于缓存组件(避免频繁创建/销毁),搭配两个专属钩子:

  • activated:缓存的组件被激活(显示)时调用。
  • deactivated:缓存的组件被停用(隐藏)时调用。
<!-- 缓存组件 -->
<keep-alive><component :is="currentComponent"></component>
</keep-alive><script>
new Vue({data() { return { currentComponent: 'ComponentA' }; },components: {ComponentA: {template: '<div>组件A</div>',activated() {console.log('组件A被激活(从缓存中取出)');// 可在此恢复状态(如刷新数据)},deactivated() {console.log('组件A被停用(存入缓存)');// 可在此暂停操作(如暂停视频播放)}}}
});
</script>

6.2 错误捕获钩子:errorCaptured

用于捕获子组件抛出的错误(Vue 2.5+),返回false可阻止错误向上传播:

new Vue({errorCaptured(err, vm, info) {console.error('捕获子组件错误:', err, '组件:', vm, '信息:', info);// 返回false阻止错误冒泡到控制台return false;}
});

七、生命周期钩子的执行顺序与实战示例

7.1 单组件执行顺序

new Vue({// 创建阶段beforeCreate() { console.log('1. beforeCreate'); },created() { console.log('2. created'); },// 挂载阶段beforeMount() { console.log('3. beforeMount'); },mounted() { console.log('4. mounted'); },// 更新阶段(触发条件:修改data)beforeUpdate() { console.log('5. beforeUpdate'); },updated() { console.log('6. updated'); },// 销毁阶段(触发条件:调用$destroy()或v-if移除)beforeDestroy() { console.log('7. beforeDestroy'); },destroyed() { console.log('8. destroyed'); }
});

执行结果
初始化时:1→2→3→4
修改数据时:5→6
销毁时:7→8

7.2 父子组件执行顺序

父组件Parent和子组件Child的钩子执行顺序:

  1. beforeCreate → 父created → 父beforeMount
  2. beforeCreate → 子created → 子beforeMount → 子mounted
  3. mounted
  4. (更新时)父beforeUpdate → 子beforeUpdate → 子updated → 父updated
  5. (销毁时)父beforeDestroy → 子beforeDestroy → 子destroyed → 父destroyed

结论:父组件等待子组件完成后,才会完成自身对应的阶段。

八、常见问题与避坑指南

8.1 避免在updated中修改数据

// 错误示例:导致无限循环
updated() {this.count++; // 修改data触发更新,再次调用updated,循环往复
}

解决方案:若需根据更新后的数据调整状态,可使用$nextTick或条件判断限制执行次数。

8.2 子组件mounted晚于父组件mounted

父组件若需等待子组件挂载完成后执行操作(如获取子组件DOM),需使用$nextTick或在子组件中通过事件通知父组件:

// 子组件
export default {mounted() {this.$emit('mounted'); // 通知父组件已挂载}
};// 父组件
<child-component @mounted="handleChildMounted"></child-component>
methods: {handleChildMounted() {console.log('子组件已挂载');}
}

8.3 清理资源是重中之重

组件销毁时若未清理定时器、事件监听等,会导致内存泄漏(页面关闭前资源一直占用):

// 错误:未清理定时器
mounted() {setInterval(() => { /* 操作 */ }, 1000); // 组件销毁后仍会执行
}// 正确:在beforeDestroy中清理
beforeDestroy() {clearInterval(this.timer);
}

九、Vue 3中的生命周期变化

Vue 3的Composition API中,生命周期钩子的使用方式有所调整,但核心逻辑一致:

  • setup():替代beforeCreatecreated(在两者之间执行)。
  • onBeforeMount:对应beforeMount
  • onMounted:对应mounted
  • onBeforeUpdate:对应beforeUpdate
  • onUpdated:对应updated
  • onBeforeUnmount:对应beforeDestroy
  • onUnmounted:对应destroyed
// Vue 3 Composition API示例
import { onMounted, onBeforeUnmount } from 'vue';export default {setup() {let timer;onMounted(() => {timer = setInterval(() => { /* 操作 */ }, 1000);});onBeforeUnmount(() => {clearInterval(timer); // 清理资源});}
};

总结

  1. 精准控制流程:在合适的阶段执行合适的操作(如created请求数据、mounted操作DOM)。
  2. 避免常见问题:如更新阶段的死循环、销毁阶段的内存泄漏。
  3. 优化组件性能:合理利用keep-alive和缓存钩子,减少不必要的创建/销毁。

若这篇内容帮到你,动动手指支持下!关注不迷路,干货持续输出!
ヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノ

http://www.dtcms.com/a/275285.html

相关文章:

  • 路由双向引入实验:将一种路由协议学习到的路由注入另一种协议中
  • Linux中的git命令
  • Spring Cloud Gateway中常见的过滤器
  • 【kubernetes】--controller(DaemonSet)
  • Git入门教程
  • 【离线数仓项目】——电商域DIM层开发实战
  • 【一起来学AI大模型】RAG系统流程:查询→向量化→检索→生成
  • 医疗AI前端开发中的常见问题分析和解决方法
  • OpenCL study - code02
  • 箭头函数(Arrow Functions)和普通函数(Regular Functions)
  • 7. 负载均衡:流量调度引擎
  • 8-day06预训练模型
  • 一个中层管理者应该看什么书籍?
  • 从就绪到终止:操作系统进程状态转换指南
  • 智能文本抽取在合同管理实战应用
  • 人事系统选型与应用全攻略:从痛点解决到效率跃升的实战指南
  • Datawhale AI夏令营:基于带货视频评论的用户洞察挑战赛上分全攻略
  • 自动驾驶线控系统与动力电池系统
  • 【天坑记录】cursor jsx文件保存时错误格式化了
  • K230摄像头配置与显示配置误解:而这根本没关系
  • 【驱动】移植CH340驱动,设置 udev 规则,解决和 BRLTTY 的冲突
  • 软件测试面试200问(附30W字面试文档)
  • 跟着Carl学算法--二叉树【3】
  • 静态路由技术
  • DeepSeek模型分析及其在AI辅助蛋白质工程中的应用-文献精读148
  • [electron]升级功能
  • CSS Grid布局和Flexbox有什么区别?
  • C语言文件读写操作详解:fgetc与feof函数的应用
  • 经典同步问题详解
  • 使用 lstrip() 和 rstrip() 方法