vue3 生命周期(长文解析)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观

在前端开发中,组件的生命周期是理解框架运行机制的核心概念之一。Vue 3 的生命周期钩子(Lifecycle Hooks)为开发者提供了对组件状态变化的精细控制能力,无论是初始化数据、操作 DOM,还是处理异步任务,都能通过这些钩子实现精准的逻辑衔接。对于编程初学者和中级开发者而言,掌握 Vue 3 生命周期不仅能提升代码的可维护性,还能帮助开发者更高效地解决实际开发中的复杂场景。本文将通过循序渐进的方式,结合实际案例和形象比喻,深入解析 Vue 3 生命周期的运作原理与最佳实践。


一、生命周期的定义与核心作用

1.1 什么是组件生命周期?

可以将组件的生命周期想象为“一段旅程”:从组件被创建到最终销毁的全过程,Vue 3 定义了多个关键节点(即生命周期钩子),允许开发者在这些节点上插入自定义逻辑。例如:

  • 出生阶段:组件被实例化并准备挂载到页面上(类似新生儿睁开眼睛的第一刻)。
  • 成长阶段:组件完成渲染并持续更新(如同儿童逐渐适应环境)。
  • 消亡阶段:组件被销毁前的清理工作(类似人离世前的遗产分配)。

1.2 Vue 3 生命周期与 Vue 2 的差异

Vue 3 的生命周期钩子命名规则与 Vue 2 有所不同,均以 on 开头,例如:

  • Vue 2:beforeMount → Vue 3:onBeforeMount
  • Vue 2:mounted → Vue 3:onMounted

这一设计更符合组合式 API(Composition API)的函数式编程风格,同时避免了与 Vue 2 的兼容性问题。


二、Vue 3 生命周期钩子详解

2.1 生命周期钩子列表与触发顺序

以下表格总结了 Vue 3 组件的生命周期钩子及其触发顺序:

生命周期钩子触发时机与核心作用
onBeforeMount组件挂载到真实 DOM 之前,此时 DOM 还未渲染,无法直接操作 DOM。
onMounted组件首次挂载到 DOM 后,此时 DOM 已渲染,适合初始化 DOM 操作或订阅事件。
onBeforeUpdate数据更新前触发,可用于在渲染前执行校验或清理旧数据。
onUpdated数据更新完成后触发,此时 DOM 已更新,适合执行依赖新 DOM 的操作。
onBeforeUnmount组件销毁前触发,用于清理资源(如定时器、事件监听器)。
onUnmounted组件完全销毁后触发,此时组件实例已不可用。

2.2 各阶段的典型应用场景与案例

2.2.1 onBeforeMountonMounted

场景:在组件挂载前后初始化资源。
案例:一个需要操作 DOM 的计数器组件:

import { ref, onBeforeMount, onMounted } from 'vue';

export default {
  setup() {
    const count = ref(0);
    let domElement;

    onBeforeMount(() => {
      // 此时 DOM 还未挂载,无法直接获取元素
      console.log('onBeforeMount: 将要挂载到 DOM,但元素尚未存在');
    });

    onMounted(() => {
      // DOM 已挂载,可以安全操作
      domElement = document.getElementById('count-display');
      console.log('onMounted: 已获取到 DOM 元素:', domElement);
    });

    return { count };
  },
};

比喻
onBeforeMount 类似于“准备行李”,而 onMounted 是“到达目的地后正式入住酒店”。

2.2.2 onBeforeUpdateonUpdated

场景:在数据更新时执行逻辑,例如异步渲染或 DOM 校正。
案例:一个动态加载图片的列表组件:

import { ref, onBeforeUpdate, onUpdated } from 'vue';

export default {
  setup() {
    const items = ref([]);

    onBeforeUpdate(() => {
      // 在更新前清理旧图片资源
      console.log('onBeforeUpdate: 即将更新,先释放内存');
      items.value.forEach(item => item.loaded = false);
    });

    onUpdated(() => {
      // 更新完成后加载新图片
      console.log('onUpdated: DOM 已更新,开始加载新图片');
      items.value.forEach(item => {
        if (!item.loaded) {
          loadImage(item.url);
          item.loaded = true;
        }
      });
    });

    return { items };
  },
};

比喻
onBeforeUpdate 是“整理房间前的物品分类”,而 onUpdated 是“新物品摆放完成后打扫卫生”。

2.2.3 onBeforeUnmountonUnmounted

场景:在组件销毁前释放资源,避免内存泄漏。
案例:一个使用定时器的倒计时组件:

import { ref, onBeforeUnmount } from 'vue';

export default {
  setup() {
    const timerId = ref(null);

    const startTimer = () => {
      timerId.value = setInterval(() => {
        // 执行计时逻辑
      }, 1000);
    };

    onBeforeUnmount(() => {
      // 清除定时器,防止组件卸载后继续执行
      clearInterval(timerId.value);
      console.log('onBeforeUnmount: 定时器已清除');
    });

    return { startTimer };
  },
};

比喻
onBeforeUnmount 是“离家前关闭所有电器”,确保资源不再被占用。


三、常见误区与最佳实践

3.1 误区一:在 onBeforeMount 中操作 DOM

由于 onBeforeMount 的触发时机早于 DOM 渲染,此时尝试获取或修改 DOM 元素会导致空值错误。
解决方案:将 DOM 操作统一放在 onMountedonUpdated 中。

3.2 误区二:错误使用 this 关键字

在 Vue 3 的组合式 API 中,组件实例不再直接暴露 this。若需访问组件属性,应通过响应式变量(如 refreactive)或 setup 上下文传递。
案例修正

// 错误写法(Vue 2 风格)
this.$refs.myElement;

// 正确写法(Vue 3 组合式 API)
import { ref } from 'vue';
const myElement = ref(null);

3.3 最佳实践:资源管理与分离逻辑

  • 清理资源:在 onBeforeUnmount 中统一处理资源释放(如定时器、事件监听器)。
  • 逻辑分离:避免在生命周期钩子中编写复杂逻辑,可将函数提取为独立方法。
  • 异步操作:在 onMounted 中发起网络请求或初始化第三方库。

四、实战案例:动态表格与生命周期结合

4.1 需求描述

创建一个动态表格组件,要求:

  1. 初始加载时请求数据。
  2. 实时更新表格内容时平滑滚动到顶部。
  3. 销毁组件时取消未完成的请求。

4.2 实现代码

import { ref, onMounted, onUpdated, onBeforeUnmount } from 'vue';
import axios from 'axios';

export default {
  setup() {
    const tableData = ref([]);
    let cancelTokenSource;

    const fetchData = async () => {
      cancelTokenSource = axios.CancelToken.source();
      try {
        const response = await axios.get('/api/data', {
          cancelToken: cancelTokenSource.token,
        });
        tableData.value = response.data;
      } catch (error) {
        if (axios.isCancel(error)) {
          console.log('Request canceled');
        } else {
          console.error('Fetch error:', error);
        }
      }
    };

    onMounted(() => {
      fetchData();
    });

    onUpdated(() => {
      // 更新后滚动到表格顶部
      const tableElement = document.querySelector('.table-container');
      tableElement.scrollTop = 0;
    });

    onBeforeUnmount(() => {
      // 取消未完成的请求
      if (cancelTokenSource) {
        cancelTokenSource.cancel('Component unmounted');
      }
    });

    return { tableData };
  },
};

五、结论

Vue 3 的生命周期钩子为开发者提供了对组件状态的精准控制能力,理解其触发顺序与核心作用,能够显著提升代码的健壮性和性能。通过本文的案例与比喻,读者可以掌握以下关键点:

  1. 钩子分阶段:出生、成长、消亡三个阶段对应不同的钩子,需根据需求选择合适的时机执行逻辑。
  2. 资源管理:避免内存泄漏,确保在组件卸载前释放所有资源。
  3. 组合式 API 优势:通过函数式编程风格,生命周期逻辑与业务代码更易分离和复用。

掌握 Vue 3 生命周期不仅是技术能力的提升,更是对前端组件化思维的深化。建议开发者通过实际项目不断实践,逐步形成对生命周期的“肌肉记忆”,最终实现高效、优雅的代码编写。

最新发布