Loading

vue的生命周期

一、总体时间线(一张图记一辈子)

 

二、四大阶段 8 个场景逐条拆解

阶段 1:实例创建(Initialization)

钩子 触发时机 能做的事 不能做的事 源码位置(vue2) 常见坑
beforeCreate 实例刚 new 完,inject、props、methods、data、computed、watch 全部未初始化 可以:打印 this,看到裸实例;注册全局事件总线 不能:访问 this.xxx、this.$data、DOM core/instance/init.js -> initLifecycle 试图读取 data 得到 undefined
setup() (仅 Vue3) 组件实例尚未创建,但 props 已解析 可以:使用 ref/reactive 创建响应式数据、调用组合式函数 不能:使用 this(不存在);访问 DOM runtime-core/component.ts 忘记 return 模板需要的绑定
created 完成了数据观测、computed、methods、watch 的初始化,但 $el 尚不可用 可以:发 ajax,访问 data/props/computed;写 this.xxx 不能:访问 DOM($el 为 undefined) core/instance/init.js -> callHook 在 SSR 中此阶段 DOM 仍未创建
调试技巧
// main.js (Vue2)
console.log('new Vue 前', Date.now());
new Vue({
  beforeCreate() { debugger; },   // Chrome 停在这里,看 call stack
  created()      { console.log('$el:', this.$el); /* undefined */ }
})

阶段 2:模板编译 + DOM 挂载(Mounting)

钩子 触发时机 能做的事 不能做的事 源码位置 常见坑
beforeMount 模板/渲染函数已编译,但虚拟 DOM 第一次 patch 前 可以:最后一次修改不触发额外渲染的数据 不能:访问真实 DOM;读取 $refs core/instance/lifecycle.js -> mountComponent 用 $refs 会得到 undefined
mounted 真实 DOM 已插入页面,$el 已指向元素 可以:操作 DOM、第三方库初始化、拿 $refs 不能:保证所有子组件也 mounted(异步子组件可能还没好) 同上 父 mounted 时子组件不一定 mounted
可视化验证
<template>
  <div ref="box">hello</div>
</template>
<script>
export default {
  beforeMount() { console.log('beforeMount', this.$refs.box); /* undefined */ },
  mounted()     { console.log('mounted', this.$refs.box);     /* <div> */ }
}
</script>

阶段 3:响应式更新(Updating)

钩子 触发时机 能做的事 不能做的事 源码位置 常见坑
beforeUpdate 数据已变,虚拟 DOM 打补丁前 可以:访问现有 DOM 状态(旧值) 建议:不要改数据,会触发二次更新 core/observer/scheduler.js -> flushSchedulerQueue 死循环:此处改同一响应式属性
updated DOM 已打补丁完成 可以:操作更新后的 DOM 不能:改数据后直接读 DOM(DOM 可能还没 flush 完) 同上 在 updated 里再改数据 → 无限循环
死循环 DEMO
export default {
  data: () => ({ n: 0 }),
  updated() {
    this.n++;   // ❌ 无限递归
  }
}
 
解决:用 nextTickwatch 替代。

阶段 4:卸载(Unmount / Teardown)

钩子 触发时机 能做的事 不能做的事 备注
beforeUnmount(Vue3) / beforeDestroy(Vue2) 组件即将被卸载 可以:清理定时器、取消订阅、解绑全局事件 不能:访问 DOM 已卸载的子组件 Vue3 改名更语义化
unmounted(Vue3) / destroyed(Vue2) 组件完全卸载,所有指令、事件监听器已解绑 可以:记录日志 不能:访问实例上的响应式数据(已解除绑定) keep-alive 缓存的组件不会触发
组合式清理示例
import { onBeforeUnmount, onUnmounted } from 'vue';
export default {
  setup() {
    const timer = setInterval(() => {}, 1000);
    onBeforeUnmount(() => clearInterval(timer));
    onUnmounted(() => console.log('组件已销毁'));
  }
}

三、keep-alive 专属钩子

Vue2 Vue3 触发时机
activated onActivated 被 keep-alive 缓存的组件再次显示
deactivated onDeactivated 被 keep-alive 缓存的组件隐藏
使用场景:在列表页缓存滚动位置、停止/恢复定时器。

四、调试与可视化

  1. Chrome DevTools → Sources → 在生命周期钩子中写 debugger;
  2. 安装 Vue Devtools → Timeline 面板直接看生命周期火焰图
  3. Vue3 的 setup() 中可加 console.trace() 查看调用栈

五、面试高频追问

问题 一句话回答
为什么 beforeCreate 拿不到 data? 此时 initState 尚未执行,data 未代理到 vm。
created 里能操作 DOM 吗? 不能,$el 还是 undefined。
父 mounted 时子组件 mounted 了吗? 不一定,子组件可以是异步组件。
updated 里再改同一份数据会怎样? 再次触发 flush,导致无限循环。
Vue3 的 setup 对应 Vue2 的哪两个钩子? 同时替代了 beforeCreate 和 created。

六、完整可运行 DEMO(Vue3 组合式)

<script type="module">
import { createApp, ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js';

createApp({
  template: `
    <button @click="count++">count: {{ count }}</button>
  `,
  setup() {
    const count = ref(0);

    console.log('setup');
    onBeforeMount(() => console.log('onBeforeMount'));
    onMounted(()   => console.log('onMounted'));
    onBeforeUpdate(() => console.log('onBeforeUpdate'));
    onUpdated(()    => console.log('onUpdated'));
    onBeforeUnmount(() => console.log('onBeforeUnmount'));
    onUnmounted(()      => console.log('onUnmounted'));

    return { count };
  }
}).mount('#app');
</script>

<div id="app"></div>
 
打开浏览器控制台,点击按钮即可看到完整生命周期日志。
posted @ 2025-07-21 17:22  Carvers  阅读(27)  评论(0)    收藏  举报