Vue2 生命周期
Vue 实例生命周期概述
Vue 实例的生命周期是指一个 Vue 实例从创建到销毁所经历的完整过程。该过程始于 new Vue() 的调用,终于实例被完全移除内存(如页面关闭或显式销毁)。整个生命周期被划分为四个逻辑阶段:创建阶段、挂载阶段、更新阶段和销毁阶段。Vue 2 提供了八个生命周期钩子函数,每个阶段执行特定的初始化或清理任务,开发者可通过生命周期钩子函数在关键节点插入自定义逻辑。

生命周期钩子函数
created 和 mounted 是最常用且必须掌握的两个钩子,分别适用于不同场景:
created钩子在实例完成数据观测、属性和方法的运算、watch/event事件配置之后被调用,此时尚未挂载 DOM,但响应式数据已就绪;mounted钩子在实例挂载完成后被调用,此时el已被新创建的vm.$el替换,并挂载到实例上,DOM 已渲染完成,可安全操作真实 DOM 元素。
关键原则:
- 若需访问或修改响应式数据(如发起初始请求),应使用
created;- 若需直接操作 DOM(如获取焦点、初始化第三方库、测量元素尺寸),必须等待
mounted及之后的钩子。
四个核心阶段及其职责
创建阶段(Creation)
- 主要任务:完成 Vue 实例的初始化配置与响应式数据准备。
- 具体工作包括:
- 解析
data、props、computed、methods等选项; - 将
data中的普通 JavaScript 对象转换为响应式数据,即通过Object.defineProperty建立 getter/setter 监听; - 初始化事件系统、计算属性依赖收集等内部机制。
- 解析
- 关键结论:此阶段结束后,所有
data属性已具备响应式能力,修改其值将触发视图更新。
挂载阶段(Mounting)
- 主要任务:将 Vue 实例的模板编译为真实 DOM,并将其挂载至指定容器元素。
- 具体工作包括:
- 编译模板(Template Compilation),生成渲染函数;
- 执行首次虚拟 DOM 渲染(Virtual DOM Reconciliation);
- 将生成的真实 DOM 插入浏览器文档流(Document Flow)。
- 关键结论:挂载完成后,页面中可见的结构即为 Vue 渲染结果,此时 DOM 节点已就绪。
更新阶段(Updating)
- 主要任务:响应数据变化,重新计算并更新视图。
- 触发条件:当响应式数据被修改时(如赋值操作、数组方法调用等)。
- 执行流程:
- 数据变更 → 触发依赖通知 → 重新执行渲染函数 → 对比新旧虚拟 DOM → 更新真实 DOM。
- 关键特性:该阶段具有循环性,可多次触发;而创建与挂载阶段仅执行一次。
销毁阶段(Destruction)
- 主要任务:释放 Vue 实例占用的资源,解除所有绑定关系。
- 具体工作包括:
- 移除所有事件监听器;
- 清理定时器、异步请求等外部资源引用;
- 解除与父组件的关联;
- 将实例标记为不可用状态。
- 关键结论:销毁后,实例不再响应数据变化,也不再参与任何 Vue 内部机制。
生命周期钩子函数详解
Vue 为每个生命周期阶段提供了两个钩子函数,分别在阶段开始前与结束后自动调用。八个标准钩子按执行顺序排列如下:
| 阶段 | 钩子名称 | 执行时机 | 典型用途 |
|---|---|---|---|
| 创建 | beforeCreate |
实例初始化之后、数据观测和事件配置之前 | 不可访问 this.data 或 this.methods;极少使用 |
created |
数据观测、属性和方法的运算已完成,data 已转为响应式,但尚未挂载到 DOM |
发送初始化请求、初始化非响应式数据、启动定时器 | |
| 挂载 | beforeMount |
模板编译完成、首次渲染前,$el 未替换为真实 DOM |
访问原始模板字符串(含插值语法);调试用途 |
mounted |
实例挂载完成,$el 已替换为真实 DOM 并插入文档 |
操作 DOM 元素、初始化第三方库(如 Chart.js)、获取 DOM 尺寸 | |
| 更新 | beforeUpdate |
数据更新导致重新渲染前,虚拟 DOM 已重新生成,但真实 DOM 尚未更新 | 获取更新前的 DOM 状态;记录性能指标 |
updated |
虚拟 DOM 重绘和打补丁完成后,真实 DOM 已更新 | 操作更新后的 DOM;避免在此触发新状态变更(防死循环) | |
| 销毁 | beforeDestroy |
实例销毁前,所有指令、事件监听器、子组件仍有效 | 清理外部资源(如 clearInterval、 clearTimeout)、解绑全局事件 |
destroyed |
实例销毁后,所有绑定、监听器、子实例均已被移除 | 仅用于确认销毁完成;不可再访问 this |
(一)重点钩子函数规范说明
created 钩子
- 执行时机:响应式数据初始化完毕后,模板编译前。
- 核心能力:
- 可安全访问
this.data、this.methods、this.computed; - 可发起 HTTP 请求并将响应数据赋值给响应式属性(如
this.list = response.data); - 支持同步/异步操作,不依赖 DOM 存在。
- 可安全访问
- 禁止行为:
- 不得访问
this.$el(此时 DOM 未生成); - 不得直接操作 DOM 节点。
- 不得访问
典型代码示例:
1
2
3
4
5
6
7
8
9
10async created() {
try {
// 发送初始化数据请求
const response = await axios.get('/api/data')
// 将响应数据赋值给 data 中的 list 属性
this.list = response.data.data
} catch (error) {
console.error('初始化请求失败', error);
}
}
mounted 钩子
- 执行时机:模板渲染完成,真实 DOM 已挂载至页面。
- 核心能力:
this.$el指向挂载容器的真实 DOM 元素;- 可通过原生 DOM API 或
this.$refs安全操作 DOM; - 适合集成依赖 DOM 的第三方库(如地图 SDK、富文本编辑器)。
- 禁止行为:
- 不应在其中修改响应式数据以触发新渲染(易引发无限循环);
- 避免执行耗时同步操作阻塞主线程。
典型代码示例:
1
2
3
4
5
6
7
8
9
10
11mounted() {
// 获取并操作 DOM 元素
const header = this.$el.querySelector('h3');
console.log('挂载后标题内容:', header.textContent);
// 初始化图表库
this.chart = new Chart(this.$refs.canvas, {
type: 'bar',
data: this.chartData
});
}
(二)其他钩子函数使用场景
beforeUpdate与updated:适用于需要对比 DOM 变更前后状态的场景,例如实现动画过渡、性能监控。需注意updated中修改数据会再次触发更新,应谨慎使用。beforeDestroy:必须用于清理非 Vue 管理的资源,包括但不限于:setInterval/setTimeout返回的 ID;addEventListener绑定的全局事件(如window.resize);- WebSocket 连接、Canvas 动画帧请求。
destroyed:仅作状态确认,无实际业务逻辑价值;实例已不可用,this上所有属性均失效。
生命周期执行顺序验证
以下为 Vue 实例完整生命周期中八个钩子的标准执行序列(按时间先后):
beforeCreatecreatedbeforeMountmounted- (用户交互触发数据变更)
beforeUpdateupdated- (调用
$destroy()或页面关闭) beforeDestroydestroyed
验证要点:
created中可访问响应式数据,beforeCreate中访问this.xxx将返回undefined;mounted中document.querySelector()可获取渲染后的 DOM,beforeMount中获取的是含插值语法的原始模板;beforeUpdate中 DOM 内容仍为旧值,updated中已同步为新值;beforeDestroy执行后,实例仍可响应事件,destroyed执行后所有 Vue 功能终止。
实践与注意事项
(一)开发规范
- 初始化请求统一置于
created:确保响应式数据已就绪,避免赋值失败。 - DOM 操作严格限定于
mounted及之后:杜绝在created或beforeMount中操作不存在的节点。 - 资源清理必须在
beforeDestroy中完成:防止内存泄漏,尤其注意定时器、事件监听器、WebSocket 连接。 - 避免在
updated中修改响应式数据:否则将触发新一轮更新,可能导致栈溢出或性能问题。
(二)常见错误规避
- 错误:在
beforeCreate中调用this.$http.get()→ 报错Cannot read property '$http' of undefined。 - 错误:在
created中执行document.getElementById('app').style.display = 'none'→ 报错Cannot read property 'style' of null(DOM 未生成)。 - 错误:在
mounted中频繁调用this.count++→ 引发连续beforeUpdate/updated循环。 - 错误:销毁组件时未清除
setInterval→ 定时器持续运行,消耗系统资源。
(三)性能优化建议
- 对于复杂组件,可在
beforeDestroy中手动取消未完成的 Axios 请求(使用 CancelToken); - 使用
v-if控制组件显示/隐藏时,若需彻底销毁,应配合key属性强制重建实例; - 大量列表渲染场景下,
updated钩子内避免执行高开销 DOM 查询,优先使用this.$refs或事件委托。
案例一:页面初始化数据请求与渲染
需求说明
实现新闻列表页面的初始化加载:进入页面时立即发送 HTTP 请求获取新闻数据,并将数据渲染至视图。
技术选型依据
- 数据请求必须在响应式系统就绪后执行;
beforeCreate钩子中this.data、this.methods等均未初始化,访问为undefined;created钩子中,data已完成响应式转换,this可正常访问所有响应式属性与方法,是发起初始化请求的唯一正确时机。
实现步骤
模拟数据
1 | # 模拟新闻假数据 |
步骤 1:定义响应式数据结构
在组件 data 选项中声明初始状态:
1 | data() { |
步骤 2:在 created 钩子中发起请求
与 data 同级定义 created 钩子,使用 axios(或其他 HTTP 库)发送请求:
1 | created() { |
注意:
- 请求结果必须显式赋值给
this.list,否则数据无法触发视图更新;response.data.data为示例接口(后端挂起的接口)返回结构,实际需根据 API 文档调整路径。
步骤 3:模板中使用 v-for 渲染列表
遍历 list 并渲染每条新闻:
1 | <ul> |
关键规范:
v-for必须配合:key使用,优先使用唯一标识item.id;若无id,可退化为index,但不推荐在列表可能重排序时使用;- 所有插值表达式
{{ }}和指令绑定:均作用于this上下文,直接访问item属性即可。
步骤 4:验证数据流完整性
- 刷新页面后,控制台输出请求成功日志;
- 浏览器开发者工具 → Vue Devtools → 查看组件
data.list已填充有效数组; - 页面 DOM 正确渲染全部新闻项,内容与响应数据一致。
完整代码
1 |
|
案例二:DOM 操作——输入框自动获取焦点
需求说明
搜索页面加载后,使搜索输入框自动获得焦点,提升用户交互效率。
技术选型依据
autofocusHTML 属性在 Vue 模板中无效,原因在于 Vue 的模板编译机制:初始 HTML 结构会被 Vue 解析并重建为虚拟 DOM,原生autofocus在重建过程中丢失;- 只有在
mounted钩子中,vm.$el已挂载且真实 DOM 完全渲染完毕,方可安全执行 DOM 查询与操作; - 此阶段确保目标元素存在于文档中,且已完成 Vue 指令(如
v-model)的解析与绑定。
实现步骤
步骤 1:模板中为输入框设置唯一标识
1 | <input type="text" id="searchInput" v-model="keyword" placeholder="请输入关键词"> |
步骤 2:在 mounted 钩子中执行 DOM 操作
1 | data:{ |
验证要点:
- 刷新页面后,光标应立即定位至输入框内;
- 在 Vue Devtools 中检查该元素是否已移除
v-model指令标记(表明模板已编译完成),确认操作时机正确。
步骤 3:替代方案(推荐使用 ref)
为增强可维护性与避免全局选择器冲突,建议采用 Vue 的 ref 特性:
1 | <input type="text" ref="searchInput" v-model="keyword" placeholder="请输入关键词"> |
1 | mounted() { |
说明:
$nextTick确保在当前 DOM 更新循环结束后执行回调,适用于依赖 DOM 更新后的操作。
完整代码
1 |
|
总结
Vue 2 的生命周期是理解框架运行机制的核心基础。掌握四个阶段的本质职责与八个钩子的精确执行时机,是编写健壮、可维护 Vue 应用的前提。开发者应明确:
created是数据初始化与请求发起的唯一安全位置;mounted是 DOM 操作与第三方库集成的起始点;beforeDestroy是释放外部资源的强制责任区。
| 钩子名称 | 执行时机 | 典型应用场景 |
|---|---|---|
created |
实例创建完成,响应式数据就绪,DOM 未挂载 | 初始化数据请求、计算属性依赖初始化、事件总线订阅、非 DOM 相关逻辑准备 |
mounted |
实例挂载完成,真实 DOM 已渲染 | 访问/操作 DOM 元素、初始化第三方 UI 库(如 Chart.js)、启动定时器、添加全局事件监听器 |
其余钩子根据具体需求选择性使用,切忌滥用。熟练运用生命周期规则,可显著提升代码可靠性、可读性与运行效率。
必须遵守的工程实践准则
- 禁止在
beforeCreate中访问this任何成员:此时data、methods、computed均未初始化; - 避免在
created中操作 DOM:this.$el为undefined,document.querySelector可能返回null; mounted是 DOM 操作的最低安全边界:此前所有钩子均不能保证 DOM 存在;- 资源清理义务:在
beforeDestroy(Vue 2)中清除定时器、解绑事件监听器、销毁第三方实例,防止内存泄漏; key属性不可省略:v-for必须提供稳定、唯一的:key,否则 Vue 无法高效复用节点,导致渲染异常或状态错乱。
进阶提示
updated钩子适用于依赖 DOM 更新后的异步操作(如滚动位置同步),但应谨慎使用以避免无限循环;activated/deactivated仅适用于<keep-alive>包裹的组件,用于管理缓存组件的激活/停用状态;- 所有生命周期钩子均为实例方法,通过
this访问组件上下文,不可定义为箭头函数(会丢失this绑定)。’’
Vue 2 综合案例:记账本(前后端交互)
项目概述
本教程基于 Vue 2 框架,实现一个完整的前后端分离记账应用——“记账清单”。该应用包含表单录入、数据列表渲染、动态图表联动及消费统计功能,所有数据均通过 HTTP 请求与后端接口交互,实现数据持久化。
系统功能模块划分为以下四部分:
- 基本渲染:页面加载时发起 GET 请求获取账单列表,并完成 DOM 渲染与消费总额计算;
- 添加功能:用户输入消费名称与价格,通过 POST 请求提交至后端,成功后同步更新前端列表;
- 删除功能:点击对应账单的删除按钮,通过 DELETE 请求移除指定记录,随后刷新列表;
- 图表联动(后续扩展):右侧图表随左侧列表数据变化实时更新(本节聚焦前三大核心功能)。
关键前提说明:
- 所有数据操作均依赖后端 RESTful 接口,前端不维护本地持久状态;
- 数据变更(增/删)仅作用于服务端,前端必须主动拉取最新数据以保持视图一致性;
- 本案例严格遵循 Vue 2 生命周期与响应式原理,不使用 Composition API 或 Vue 3 特性。
快速开始
页面模板
1 | <div class="container"> |
1 | <style> |
后端模拟数据
生成模拟数据
1 | import json |
提供api
1 | from fastapi import APIRouter, Query, Body, Path |
基本渲染功能实现
功能目标
页面初始化时自动获取账单数据,渲染表格列表,并实时计算消费总额。
实现步骤
步骤 1:在 created 钩子中发起初始请求
1 | new Vue({ |
关键点说明:
- 必须在
created生命周期钩子中调用,确保组件实例创建后立即执行;await必须置于async函数内,否则语法报错;res.data.data是接口约定的数据路径,需根据实际响应结构调整。
步骤 2:模板中渲染账单列表
1 | <table> |
关键点说明:
v-for使用item.id作为:key,确保列表更新的高效性;- 编号采用
index + 1实现连续序号;- 价格保留两位小数:
.toFixed(2);- 动态类名控制:
:class="{ red: item.price > 500 }"实现高亮逻辑。
步骤 3:使用计算属性实现消费总额统计
1 | computed:{ |
模板中引用:
1 | <div>消费总计:{{ totalPrice }} 元</div> |
关键点说明:
computed属性具备响应式依赖追踪,当list变更时自动重新求值;reduce初始值设为0,避免空数组导致NaN;- 最终结果统一
.toFixed(2)格式化。
添加功能实现
功能目标
用户填写消费名称与价格,点击“添加账单”按钮后,向后端提交新记录,并刷新列表。
实现步骤
步骤 1:收集表单数据(双向绑定)
1 | new Vue({ |
模板绑定:
1 | <input v-model.trim="name" class="input-name" placeholder="请输入消费名称" /> |
关键点说明:
v-model.trim自动去除首尾空格;v-model.number强制转换为数值类型,避免字符串拼接错误。
步骤 2:定义添加方法并发送 POST 请求
1 | methods: { |
关键点说明:
- 必须执行
this.getList():添加操作仅修改服务端数据,前端需主动拉取最新状态;- 校验逻辑前置,避免无效请求;
- 成功后清空输入框,提升用户体验。
删除功能实现
功能目标
点击某条账单的“删除”按钮,向后端发送 DELETE 请求移除该记录,并刷新列表。
实现步骤
步骤 1:为删除按钮绑定事件并传参
1 | <td> |
步骤 2:定义删除方法并发送 DELETE 请求
1 | methods: { |
关键点说明:
- DELETE 请求的
id通过 URL 路径参数传递(/api/bills/123);- 使用
confirm()提供二次确认,防止误操作;- 必须执行
this.getList():删除操作仅影响服务端,前端需同步最新数据。
ECharts图表的实现
环境依赖
通过CDN引入ECharts
1 | <script src="https://cdn.jsdelivr.net/npm/echarts@6.1.0/dist/echarts.min.js"></script> |
Vue 实例结构要求
data中定义用于存储业务数据的响应式属性(如list)methods中封装数据请求与视图更新逻辑- 正确使用 Vue 生命周期钩子控制执行时序
初始化饼图实例(mounted 钩子中执行)
ECharts 图表初始化必须在 DOM 元素挂载完成后进行。Vue 的 mounted 钩子是唯一安全执行 DOM 操作的时机。
1 | new Vue({ |
关键说明:
this.myChart必须挂载到 Vue 实例上(this),否则无法在其他方法中访问;- 初始化配置中
series.data设为[],为后续动态更新预留接口;- 严禁在
created或beforeMount中初始化图表,此时 DOM 尚未生成,querySelector将返回null。
动态更新饼图数据(数据获取后触发)
图表数据需随业务数据变化实时同步。更新操作通过 setOption 方法完成,仅需传入待更新的配置片段,无需重复设置已存在的选项。
数据格式映射规则
假设原始账单数据结构为:
1 | [ |
ECharts series.data 要求格式为:
1 | [ |
使用 map 方法完成格式转换
1 | // 在 methods 中定义数据请求方法 |
技术要点:
map返回新数组,不修改原数组,符合 Vue 响应式系统对不可变数据的要求;- 箭头函数中直接返回对象字面量时,必须用圆括号包裹
({}),避免语法解析错误;setOption调用位置应在请求成功回调内,确保数据已更新至this.list。
配置项详解(核心字段说明)
| 配置项 | 类型 | 说明 | 是否必需 |
|---|---|---|---|
title.text |
String | 图表主标题文本 | 否(可省略) |
tooltip.trigger |
String | 提示框触发类型,'item' 表示悬停扇形区域时显示 |
是 |
legend.orient |
String | 图例布局方向,'vertical' 为垂直排列 |
否(默认 'horizontal') |
legend.left |
String/Number | 图例水平位置,'left' 表示靠左对齐 |
否 |
series.type |
String | 图表类型,饼图固定为 'pie' |
是 |
series.radius |
String/Array | 饼图半径,支持 '50%' 或 [‘30%’, ‘70%’](环形图) |
是 |
series.data |
Array | 数据项数组,每项为 { value: Number, name: String } 结构 |
是 |
注意:所有未在
setOption中显式传入的配置项将保持初始化时的值,无需重复设置。
生命周期与执行时序规范
| 阶段 | 执行时机 | 推荐钩子 | 典型操作 |
|---|---|---|---|
| 初始化准备 | 组件实例创建后、DOM 挂载前 | created |
发起首次数据请求、定义响应式数据 |
| DOM 操作入口 | 真实 DOM 节点已挂载完毕 | mounted |
初始化第三方库(ECharts、Swiper 等)、绑定事件监听器 |
| 数据驱动更新 | 业务数据变更后 | methods 内部 |
调用 setOption、触发 v-for 重渲染 |
反例警示:
- 在
created中调用echarts.init()→ 报错Cannot read property 'init' of undefined;- 在
data中直接声明myChart: null→ 无法在mounted外访问实例;setOption传入完整配置对象而非增量更新 → 性能浪费且易覆盖已有配置。
总结与实践建议
- 图表初始化必须与 DOM 生命周期严格对齐:
mounted是 ECharts 初始化的唯一合法入口; - 数据更新遵循“最小化原则”:仅通过
setOption传递变更字段,避免全量重绘; - 数据转换是前端职责:业务 API 返回的数据结构通常需经
map/filter等方法适配可视化需求; - 封装复用性逻辑:将
getList抽离为独立方法,在添加、删除操作后统一调用,保障视图一致性; - 性能优化延伸:当数据量较大时,可结合
echarts.connect实现多图表联动,或启用renderAsImage提升渲染效率。