Skip to content

✨ 生命周期 👌

本章目标

  • 理解组件从创建到销毁的关键时间点与钩子作用
  • 熟悉常用钩子:onMountedonUpdatedonUnmounted
  • 正确在钩子中进行 DOM 访问、事件清理与数据请求
  • 避免与 Vue2 选项式钩子的混用造成时机混乱

什么是生命周期

组件从创建到最终销毁,会依次经历若干阶段(如挂载、更新、卸载),框架在这些阶段暴露“钩子函数”供开发者在合适时机执行业务逻辑。

完整的生命周期图如下:

使用时机要点

  • 只有在 onMounted 触发后才能安全访问真实 DOM(如 el.value
  • onUnmounted 中做清理(移除事件监听、取消定时器、断开订阅)
  • 只在需要的阶段做工作:避免在 onUpdated 中执行昂贵计算导致卡顿

快速入门:onMounted

onMounted 在组件初次完成渲染并插入 DOM 后触发,常用于发起数据请求或读取 DOM。

vue
<template>
  <div>
    <h1>用户列表</h1>
    <ul v-if="!loading">
      <li v-for="user in users" :key="user.id">
        {{ user.name }} - {{ user.email }}
      </li>
    </ul>
    <div v-else>加载中...</div>
    <div v-if="error">出错啦:{{ error }}</div>
  </div>
</template>

<script setup>
import { ref, onMounted } from "vue";

const users = ref([]);
const loading = ref(false);
const error = ref(null);

onMounted(async () => {
  loading.value = true;
  try {
    const res = await fetch("https://jsonplaceholder.typicode.com/users");
    if (!res.ok) throw new Error("请求失败");
    users.value = await res.json();
  } catch (err) {
    error.value = err.message;
  } finally {
    loading.value = false;
  }
});
</script>

在挂载后访问 DOM

vue
<template>
  <h2 ref="title">Hello</h2>
</template>

<script setup>
import { ref, onMounted } from "vue";
const title = ref(null);

onMounted(() => {
  // 此处才能安全访问真实 DOM
  console.log("标题宽度:", title.value.offsetWidth);
});
</script>

常用钩子一览

  • onBeforeMount:组件挂载前(尚未渲染 DOM)
  • onMounted:组件挂载后(可以访问 DOM)
  • onBeforeUpdate:响应式更新前(虚拟 DOM 已生成、未打补丁)
  • onUpdated:更新完成后(DOM 已与状态同步)
  • onBeforeUnmount:卸载前(即将移除组件)
  • onUnmounted:卸载后(做资源清理)
  • onActivated / onDeactivated:配合 KeepAlive 缓存组件时的激活/失活
  • onErrorCaptured:捕获子组件渲染/事件中的错误

示例:在卸载时清理副作用

vue
<script setup>
import { onMounted, onUnmounted } from "vue";

const handler = () => console.log("scrolling");

onMounted(() => {
  window.addEventListener("scroll", handler);
});

onUnmounted(() => {
  window.removeEventListener("scroll", handler);
});
</script>

示例:更新后读取最新布局

vue
<script setup>
import { ref, onUpdated } from "vue";
const count = ref(0);

onUpdated(() => {
  // 组件已更新到最新状态,可进行依赖最新 DOM 的逻辑
  console.log("组件已更新到最新 count:", count.value);
});
</script>

示例:KeepAlive 的激活与失活

vue
<script setup>
import { onActivated, onDeactivated } from "vue";

onActivated(() => {
  console.log("组件被激活(再次展示)");
});

onDeactivated(() => {
  console.log("组件被失活(被缓存不再展示)");
});
</script>

示例:捕获错误

vue
<script setup>
import { onErrorCaptured } from "vue";

onErrorCaptured((err, instance, info) => {
  console.error("捕获错误:", err, info);
  // 返回 false 可阻止错误继续向上传播
  return false;
});
</script>

常见误区

  • setup 之外或异步回调中直接使用生命周期钩子,可能导致未注册或执行时机异常
  • onUpdated 中做重计算或频繁 DOM 操作,会造成性能问题;必要时做节流/防抖
  • SSR 场景下没有真实 DOM,需在客户端挂载后再做 DOM 相关逻辑

与 Vue2 生命周期的关系

Vue3 仍兼容 Vue2 的选项式生命周期(如 mountedupdated 等)。但在同一组件内混用两套钩子会增加复杂度与时机差异。

vue
<template>
  <div>Vue2 与 Vue3 钩子并存示例(不推荐混用)</div>
  <p>打开控制台查看执行顺序</p>
  <button @click="msg = msg + '!'">更新</button>
  <p>{{ msg }}</p>
</template>

<script>
import { onMounted, onUpdated } from "vue";
export default {
  data() {
    return { msg: "Hello" };
  },
  mounted() {
    console.log("Vue2 mounted");
  },
  updated() {
    console.log("Vue2 updated");
  },
  setup() {
    onMounted(() => {
      console.log("Vue3 onMounted");
    });
    onUpdated(() => {
      console.log("Vue3 onUpdated");
    });
  },
};
</script>

建议

  • 在同一组件内优先统一为“组合式 API”的生命周期钩子,便于维护与类型推断
  • 若使用选项式风格,可保持整组件都采用选项式,避免混合写法

小结

  • 生命周期钩子提供了在关键阶段执行逻辑的能力
  • onMounted 做首屏数据与 DOM 访问;onUnmounted 做清理;onUpdated 做变更后处理
  • 合理选择钩子并控制副作用范围,有助于可维护与高性能的组件实现