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 对象当中,与 datamethods 平级。

复制代码
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 必须存在
  • eltemplate 同时存在,优先选择 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 面试题:请求数据应该在哪个钩子里?

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

答案 :请求数据可以在 createdmounted 中发起,但最好在 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应用。下一篇文章我们将继续学习组件化开发,敬请期待!

相关推荐
研☆香2 小时前
一个简单的气泡弹窗制作
javascript
xy34532 小时前
axure9.0 如何打造一个计时器(简单版)
前端·ui·html·axure·原型·产品设计
大模型丫丫2 小时前
用 TypeScript、NestJS 和 LangGraph 搭建一个可控的 AI Agent
javascript·人工智能·typescript
IMPYLH2 小时前
HTML 的 <pre> 元素
前端·javascript·html
shmily麻瓜小菜鸡3 小时前
JavaScript / TypeScript 易踩坑知识点 —— 异步编程类
开发语言·javascript·typescript
tsumikistep3 小时前
【前端】Vue + Axios 跨域问题实战:7070 代理转发 8080 + 401 报错分析(黑马外卖)
前端·javascript·vue.js
lichenyang4533 小时前
鸿蒙首页从等高商品 Grid 到双列瀑布流:同一张图也能做出小红书式浏览节奏
前端
里欧跑得慢3 小时前
Flutter主题与样式详解
前端·css·flutter·web
计算机魔术师3 小时前
TPU推理性价比翻50%,NVIDIA的护城河要见底了?
前端