Vue2 生命周期完全指南:从创建到销毁的8个钩子函数

在前几篇文章中,我们已经学习了Vue的核心概念、指令语法、事件处理、响应式原理等知识。今天我们将深入Vue的生命周期,理解一个Vue实例从创建到销毁的完整过程。掌握生命周期,你就能在正确的时机做正确的事情。

一、什么是生命周期?

生命周期:一个事物从出生到最终的死亡,整个经历的过程叫做生命周期。

生活中的生命周期

以人的生命周期为例:

  1. 出生:打疫苗
  2. 3岁:上幼儿园
  3. 6岁:上小学
  4. 12岁:上初中
  5. 15岁:上高中
  6. 60岁:退休
  7. 临终:立遗嘱
  8. 死亡:火化

可以看到,在这个生命线上有很多不同的时间节点,在每个时间节点上我们去做不同的事情。

Vue的生命周期

Vue的生命周期指的是:vm对象从创建到最终销毁的整个过程。

在这个过程中,Vue会在不同的时间节点自动调用不同的函数,这些函数被称为生命周期钩子函数。

复制代码
虚拟DOM在内存中就绪时 → 调用a函数
虚拟DOM转换成真实DOM渲染到页面时 → 调用b函数
Vue的data发生改变时 → 调用c函数
Vue实例被销毁时 → 调用x函数

关键特点:这些钩子函数不需要程序员手动调用,由Vue自动调用。程序员只需按照需求编写函数体,到了那个时间点自动就会执行。

二、掌握Vue生命周期有什么用?

研究Vue的生命周期主要是研究:在不同的时刻Vue做了哪些不同的事情。

例如:在vm被销毁之前,需要将绑定到元素上的自定义事件全部解绑。那么这个解绑的代码就需要找到一个合适的地方来写------可以写到 beforeDestroy() 这个函数中。

核心目标:研究在哪个时刻调用了哪个钩子函数,以及在该钩子函数中可以做什么事情。

三、Vue生命周期的4个阶段8个钩子

Vue的生命周期可以被划分为4个阶段,每个阶段调用两个钩子函数:

阶段 钩子函数 调用时机
初始阶段​ beforeCreate() 创建前
created() 创建后
挂载阶段​ beforeMount() 挂载前
mounted() 挂载后
更新阶段​ beforeUpdate() 更新前
updated() 更新后
销毁阶段​ beforeDestroy() 销毁前
destroyed() 销毁后

命名规律 :每个阶段的钩子函数命名遵循 beforeXxx() 和 xxxed() 的规律。

钩子函数写在哪里?

直接写在Vue构造函数的 options 对象当中,与 data、methods 平级。

复制代码
const vm = new Vue({
    el: "#app",
    data: {
        msg: "Vue生命周期",
        counter: 1
    },
    methods: {
        add() {
            console.log("add....");
            this.counter++;
        },
        destroy() {
            this.$destroy();
        }
    },
    watch: {
        counter() {
            console.log("counter被监视了一次!");
        }
    },
    
    // 生命周期钩子函数
    beforeCreate() {
        console.log("beforeCreate");
    },
    created() {
        console.log("created");
    },
    beforeMount() {
        console.log("beforeMount");
    },
    mounted() {
        console.log("mounted");
    },
    beforeUpdate() {
        console.log("beforeUpdate");
    },
    updated() {
        console.log("updated");
    },
    beforeDestroy() {
        console.log("beforeDestroy");
    },
    destroyed() {
        console.log("destroyed");
    }
});

四、初始阶段详解

4.1 执行流程

  1. 创建Vue实例 vm(生命的起点)
  2. 初始化事件对象和生命周期
  3. 调用 beforeCreate() 钩子函数
  4. 初始化数据代理和数据监测
  5. 调用 created() 钩子函数
  6. 编译模板语句生成虚拟DOM

4.2 关于el和template的判断逻辑

复制代码
el是否存在?
├── el存在
│   ├── template是否存在?
│   │   ├── template存在 → 编译template模板语句
│   │   └── template不存在 → 编译el模板语句
│   └── 
└── el不存在
    └── 等待手动调用 vm.$mount(el) → 回到上面继续判断

结论:

  • 流程要想继续,el 必须存在
  • el 和 template 同时存在,优先选择 template
  • 如果没有 template,则编译 el 模板语句

4.3 钩子函数详解

beforeCreate() --- 创建前

此时数据代理和数据监测尚未创建完毕,无法访问 data 中的数据和 methods 中的方法。

复制代码
beforeCreate() {
    console.log("beforeCreate", this.counter); // undefined
    // this.add(); // ❌ 报错:不存在
}

created() --- 创建后

数据代理和数据监测创建完毕,可以访问 data 中的数据和 methods 中的方法。

复制代码
created() {
    console.log("created", this.counter); // 1
    this.add(); // ✅ 可以调用
}

4.4 该阶段适合做什么?

钩子函数 适合做的事情
beforeCreate 添加loading效果;自定义属性放到this身上(如全局事件总线)
created 结束loading效果;发送网络请求获取数据;添加定时器

五、挂载阶段详解

5.1 执行流程

  1. 调用 beforeMount() 钩子函数
  2. 给vm追加 $el 属性,代替 el,$el 代表真实的DOM元素
  3. 调用 mounted() 钩子函数

5.2 钩子函数详解

beforeMount() --- 挂载前

此时页面还未渲染,真实DOM还未生成。此时操作数据,不会最终显示在页面中。

复制代码
beforeMount() {
    console.log("beforeMount");
    // debugger // 可以用断点查看此时页面状态
}

mounted() --- 挂载后

真实DOM生成,页面渲染完成,可以操作页面的DOM元素了。

复制代码
mounted() {
    console.log("mounted");
    // 可以获取真实DOM元素
    console.log(this.$el);
    console.log(this.$el instanceof HTMLElement); // true
}

5.3 该阶段适合做什么?

钩子函数 适合做的事情
mounted 操作页面的DOM元素;请求数据

5.4 面试题:请求数据应该在哪个钩子里?

问题:请求数据是在哪个钩子里?原因是什么?

答案 :请求数据可以在 created 或 mounted 中发起,但最好在 created 中。

原因 :mounted 已经挂载完毕,数据请求回来后需要重新渲染页面,可能会出现闪屏的情况。而在 created 中发起请求,数据回来得更早,可以避免不必要的DOM操作。

六、更新阶段详解

6.1 执行流程

  1. data 发生变化(这是该阶段开始的标志)
  2. 调用 beforeUpdate() 钩子函数
  3. 虚拟DOM重新渲染和修补
  4. 调用 updated() 钩子函数

6.2 钩子函数详解

beforeUpdate() --- 更新前

此时只是内存中的数据发生变化,页面还未更新。

复制代码
beforeUpdate() {
    console.log("beforeUpdate");
}

updated() --- 更新后

页面已经更新完成。

复制代码
updated() {
    console.log("updated");
}

6.3 该阶段适合做什么?

钩子函数 适合做的事情
beforeUpdate 更新之前访问现有DOM;手动移除已添加的事件监听器
updated 页面更新后,对数据做统一处理

七、销毁阶段详解

7.1 执行流程

  1. vm.$destroy() 方法被调用(这是该阶段开始的标志)
  2. 调用 beforeDestroy() 钩子函数
  3. 卸载子组件和监视器、解绑自定义事件监听器
  4. 调用 destroyed() 钩子函数

7.2 钩子函数详解

beforeDestroy() --- 销毁前

此时Vue实例还在,虽然vm上的监视器、子组件、自定义事件监听器还在,但它们已经都不能用了。此时修改data也不会重新渲染页面了。

复制代码
beforeDestroy() {
    console.log("beforeDestroy");
    console.log(this); // vm实例还在
    this.counter = 1000; // 修改数据,但页面不会更新
}

destroyed() --- 销毁后

虽然翻译为"已销毁",但此时Vue实例还在,空间并没有释放,只不过马上要释放了。"已销毁"指的是vm对象上所有的东西都已经解绑完成了。

复制代码
destroyed() {
    console.log("destroyed");
    console.log(this); // vm实例还在,但功能已失效
}

7.3 该阶段适合做什么?

钩子函数 适合做的事情
beforeDestroy 销毁前的准备工作,如同人临终前写遗嘱。清除定时器、解绑自定义事件、解除watch监听等

八、生命周期完整流程图

复制代码
new Vue()
    ↓
初始化事件和生命周期
    ↓
beforeCreate() ← 数据代理/监测尚未创建
    ↓
初始化数据代理和数据监测
    ↓
created() ← 可以访问data和methods了
    ↓
判断是否有el选项
    ↓
    ├── 有el → 判断是否有template
    │   ├── 有template → 编译template
    │   └── 无template → 编译el外部HTML
    │
    └── 无el → 等待vm.$mount(el)调用
                ↓
            (回到上面继续)
    ↓
beforeMount() ← 页面尚未渲染
    ↓
创建vm.$el并替换el
    ↓
mounted() ← 页面已渲染,可以操作DOM
    ↓
    (等待数据变化)
    ↓
数据发生变化
    ↓
beforeUpdate() ← 数据已变,页面未更新
    ↓
虚拟DOM重新渲染和修补
    ↓
updated() ← 页面已更新
    ↓
    (等待vm.$destroy()调用)
    ↓
vm.$destroy()被调用
    ↓
beforeDestroy() ← 清理工作(清除定时器、解绑事件等)
    ↓
卸载子组件、监视器、自定义事件
    ↓
destroyed() ← 实例功能已失效

九、各阶段总结

阶段 钩子函数 特点 适合做什么
初始阶段​ beforeCreate 无法访问data和methods 添加loading、全局事件总线
created 可访问data和methods 请求数据、结束loading、添加定时器
挂载阶段​ beforeMount 页面未渲染 最后的DOM操作准备
mounted 页面已渲染 操作DOM、请求数据
更新阶段​ beforeUpdate 数据已变,页面未更新 访问旧DOM、移除事件监听
updated 页面已更新 数据统一处理
销毁阶段​ beforeDestroy 实例仍在,但功能即将失效 清除定时器、解绑事件、解除watch
destroyed 实例功能已解绑 最终清理

十、最佳实践

10.1 请求数据的最佳位置

复制代码
// ✅ 推荐:在created中请求数据
created() {
    axios.get('/api/data').then(res => {
        this.list = res.data;
    });
}

// 可选:在mounted中请求数据
mounted() {
    axios.get('/api/data').then(res => {
        this.list = res.data;
    });
}

10.2 清理资源的最佳位置

复制代码
data() {
    return {
        timer: null
    };
},
created() {
    this.timer = setInterval(() => {
        console.log('定时器执行中...');
    }, 1000);
},
beforeDestroy() {
    // ✅ 清除定时器
    clearInterval(this.timer);
    this.timer = null;
    
    // ✅ 解绑自定义事件
    // this.$off('customEvent');
    
    // ✅ 解除watch监听
    // this.$watch('xxx', () => {});
}

10.3 操作DOM的最佳位置

复制代码
mounted() {
    // ✅ 此时可以安全地操作DOM
    this.$refs.myDiv.style.color = 'red';
    
    // 或者使用nextTick确保DOM更新完成
    this.$nextTick(() => {
        // DOM已更新
    });
}

掌握了生命周期,你就能在正确的时机做正确的事情,写出更加健壮和高效的Vue应用。下一篇文章我们将继续学习组件化开发,敬请期待!

相关推荐
福兮说5 小时前
前端下载大文件,点了按钮半天没反应:fetch + blob 的三个问题和三种替代写法
前端·javascript·性能优化·文件下载
GreenTea5 小时前
我把 Agent 的 while 循环拆掉了:一种你可能没想到的 Agent 架构
前端·后端·架构
科技观察哨6 小时前
六足平台选型与纳米定位系统集成:HEB-640六自由度位移台在半导体光刻对准中的参数边界与国产替代评估
前端·人工智能
niucloud-admin6 小时前
JAVA V6 多商户商城 开发文档——web前端
前端
颜进强6 小时前
16 · NestJS LifecycleEvents 生命周期事件:五个钩子、三条边界,和 rag-server 的优雅停机
前端·后端·ai编程
今年下半年8 小时前
【VUE】整合腾讯地图、自定义区域边界、村委名称及资产统计(放大显示资产点位)
前端·javascript·vue.js
郑州光合科技余经理8 小时前
同城电商系统:库存变更怎么同步到订单
java·开发语言·前端·后端·uni-app·php·ai编程
前端snow10 小时前
ai agent --- DeepAgents 中间件
前端
data analyse 45610 小时前
报表能自定义的国内统计工具有哪些
前端·数据分析