引言:生命周期的本质思考

        前面讲了一些ref函数,接下来我们要讲不管v2还是v3还是其他的框架都是重要的一个概念。在深入探讨 Vue 生命周期之前,让我们先思考一个根本问题:为什么现代前端框架都需要生命周期概念?

想象一下建造一栋精装房的完整流程:

  •  蓝图规划(设计图纸、准备材料)

  • 主体施工(搭建结构、硬装完成)

  •  软装入住(家具布置、开始使用)

  • 日常维护(维修更新、功能升级)

  • 拆迁重建(清理现场、资源回收)

Vue 组件的生命周期正是这样的"建筑时序图",它为开发者提供了在组件不同阶段执行代码的精确时间窗口。

一、Vue 2 生命周期:经典的八阶段模型

完整的生命周期图谱

创建阶段(Creation Phase):
  beforeCreate → created

挂载阶段(Mounting Phase):
  beforeMount → mounted

更新阶段(Update Phase):
  beforeUpdate → updated

销毁阶段(Destruction Phase):
  beforeDestroy → destroyed

各阶段深度解析

1. 创建阶段 - 组件的"孕育期"
export default {
  beforeCreate() {
    // 组件实例刚被创建,但数据观测、事件配置都未初始化
    console.log('beforeCreate - 胚胎形成,但尚无生命特征');
    console.log(this.message); // undefined
    console.log(this.$el);     // undefined
  },
  created() {
    // 组件实例创建完成,数据观测、计算属性、方法已配置
    // 但 DOM 树还未生成,模板编译未开始
    console.log('created - 生命体征已具备,但尚未有形体');
    console.log(this.message); // 可访问数据
    this.fetchData();         // 可调用方法
    console.log(this.$el);    // undefined,DOM 仍未就绪
  }
}

实际应用场景

  • created:API 数据请求、事件监听初始化、非 DOM 依赖的初始化逻辑

2. 挂载阶段 - 组件的"诞生礼"
export default {
  beforeMount() {
    // 模板编译完成,虚拟 DOM 已创建,但尚未挂载到真实 DOM
    console.log('beforeMount - 形体已设计完成,等待降临现实世界');
    console.log(this.$el); // 仍然是编译前的模板
  },
  mounted() {
    // 组件已挂载到真实 DOM,可以访问 DOM 元素
    console.log('mounted - 已降临现实世界,可与用户交互');
    console.log(this.$el); // 真实的 DOM 元素
    this.initChart();     // 初始化需要 DOM 的第三方库
  }
}
3. 更新阶段 - 组件的"成长轨迹"
export default {
  beforeUpdate() {
    // 数据变化,虚拟 DOM 重新渲染和打补丁之前
    console.log('beforeUpdate - 即将成长变化,当前状态:', this.message);
  },
  updated() {
    // 虚拟 DOM 重新渲染和打补丁完成
    console.log('updated - 成长完成,新状态:', this.message);
    // 可执行依赖新 DOM 的操作
  }
}
4. 销毁阶段 - 组件的"生命终结"
export default {
  beforeDestroy() {
    // 实例销毁之前,此时实例仍然完全可用
    console.log('beforeDestroy - 生命即将终结,整理遗物');
    this.clearTimers();   // 清除定时器
    this.removeListeners(); // 移除事件监听
  },
  destroyed() {
    // 实例销毁后,所有绑定解除,子实例也被销毁
    console.log('destroyed - 生命已终结,一切归于虚无');
  }
}

二、父组件 vs 子组件:生命周期执行顺序

完整的执行顺序流程

父组件 beforeCreate
父组件 created
父组件 beforeMount

    -> 开始处理子组件
    子组件 beforeCreate
    子组件 created  
    子组件 beforeMount
    子组件 mounted

父组件 mounted

三、Vue 3 生命周期:组合式 API 的现代化重构

生命周期对照表

Vue 2 Vue 3 变化说明
beforeCreate  移除 被 setup() 替代
created  移除 被 setup() 替代
beforeMount onBeforeMount 功能相同,命名更语义化
mounted onMounted 功能相同,组合式 API 风格
beforeUpdate onBeforeUpdate 功能相同
updated onUpdated 功能相同
beforeDestroy onBeforeUnmount 命名更准确(卸载而非销毁)
destroyed onUnmounted 命名更准确

Vue 3 组合式 API 实现

<template>
  <div>{{ message }}</div>
</template>

<script setup>
import { 
  ref, 
  onBeforeMount, 
  onMounted, 
  onBeforeUpdate, 
  onUpdated,
  onBeforeUnmount,
  onUnmount
} from 'vue'

const message = ref('Hello Vue 3!')

// 挂载阶段
onBeforeMount(() => {
  console.log('onBeforeMount - 即将挂载到 DOM')
})

onMounted(() => {
  console.log('onMounted - 已挂载完成,可访问 DOM')
  // DOM 操作、第三方库初始化
})

// 更新阶段
onBeforeUpdate(() => {
  console.log('onBeforeUpdate - 数据变化,更新前')
})

onUpdated(() => {
  console.log('onUpdated - 更新完成')
})

// 卸载阶段
onBeforeUnmount(() => {
  console.log('onBeforeUnmount - 组件即将卸载')
  // 清理工作
})

onUnmounted(() => {
  console.log('onUnmounted - 组件已卸载')
})
</script>

四、深度探讨:为什么 Vue 3 移除了创建阶段钩子?

设计哲学转变

Vue 2 的问题

// Vue 2 中需要在不同生命周期分散逻辑
export default {
  data() {
    return { user: null, posts: [] }
  },
  created() {
    this.fetchUser()   // 用户数据请求
  },
  mounted() {
    this.fetchPosts()  // 文章数据请求
    this.initPlugin()  // 插件初始化
  },
  destroyed() {
    this.cleanup()     // 清理工作
  }
}

Vue 3 的解决方案

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'

// 相关逻辑集中管理
const user = ref(null)
const posts = ref([])

// 用户相关逻辑
const fetchUser = async () => { 
  user.value = await api.getUser() 
}

// 文章相关逻辑  
const fetchPosts = async () => {
  posts.value = await api.getPosts()
}

// 插件相关逻辑
const initPlugin = () => { /* ... */ }
const cleanup = () => { /* ... */ }

// 生命周期中组织逻辑
onMounted(async () => {
  await fetchUser()   // 原本在 created 的逻辑
  await fetchPosts()  // 原本在 mounted 的逻辑
  initPlugin()        // 原本在 mounted 的逻辑
})

onUnmounted(() => {
  cleanup()           // 清理逻辑
})
</script>

移除 beforeCreate 和 created 的真正原因

  1. 逻辑重复性setup() 函数本身就是在组件创建阶段执行

  2. 代码组织性:组合式 API 让相关逻辑自然聚集,无需人为分割

  3. 简化模型:减少生命周期概念,降低学习成本

<script setup>
// 在 setup 中直接编写原本在 created 中的逻辑
const data = ref('这是数据初始化')
const method = () => { /* 方法定义 */ }

// 相当于 Vue2 的 created 阶段
console.log('这部分代码在组件创建时立即执行')

// 需要 DOM 的操作放到 onMounted 中
onMounted(() => {
  console.log('需要 DOM 的操作在这里执行')
})
</script>

五、常用生命周期钩子实战指南

1. onMounted - 最常用的钩子

<template>
  <div ref="chartContainer"></div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import * as echarts from 'echarts'

const chartContainer = ref(null)
let chartInstance = null

onMounted(() => {
  // 初始化图表
  chartInstance = echarts.init(chartContainer.value)
  
  // 添加窗口 resize 监听
  window.addEventListener('resize', handleResize)
  
  // 获取初始数据
  fetchData()
})

const handleResize = () => {
  chartInstance?.resize()
}
</script>

2. onUpdated - 谨慎使用的钩子

<script setup>
import { ref, onUpdated, nextTick } from 'vue'

const list = ref([])

onUpdated(() => {
  // 注意:这里要避免无限循环更新!
  if (list.value.length > 10) {
    // 执行某些依赖最新 DOM 状态的操作
  }
})

// 更好的做法:使用 watch 或 nextTick
const addItem = async () => {
  list.value.push(new Item())
  await nextTick()
  // DOM 更新后执行的操作
}
</script>

3. onBeforeUnmount - 资源清理专家

<script setup>
import { onBeforeUnmount } from 'vue'

let timer = null

// 设置定时器
timer = setInterval(() => {
  console.log('定时执行')
}, 1000)

onBeforeUnmount(() => {
  // 清理工作
  clearInterval(timer)
  timer = null
  
  // 移除事件监听
  window.removeEventListener('resize', handleResize)
  
  // 取消未完成的请求
  // abortController.abort()
})
</script>

六、版本对比与迁移策略

Vue 2 → Vue 3 生命周期迁移指南

// Vue 2
export default {
  created() {
    this.initData()
  },
  mounted() {
    this.initDOM()
  },
  beforeDestroy() {
    this.cleanup()
  }
}

// Vue 3 等效写法
<script setup>
import { onMounted, onBeforeUnmount } from 'vue'

// created 逻辑直接写在 setup 中
initData()

onMounted(() => {
  initDOM()
})

onBeforeUnmount(() => {
  cleanup()
})
</script>

总结:生命周期的演进哲学

Vue 3 的生命周期设计体现了前端框架发展的几个重要趋势:

  1. 关注点分离 → 逻辑关注点聚合

  2. 时间轴驱动 → 逻辑功能驱动

  3. 配置化编程 → 组合式编程

  4. 概念复杂性 → 模型简化

给开发者的建议

  •  Vue 3 项目:优先使用组合式 API,享受更自然的代码组织方式

  • 迁移项目:理解设计哲学变化,不仅仅是 API 的机械替换

  • 学习路径:先理解生命周期的本质,再掌握具体 API

  • 最佳实践:在 setup 中组织逻辑,在生命周期中安排执行时机

生命周期的演进不仅是 API 的变化,更是前端开发思维模式的升级。理解这背后的设计,比记忆具体的 API 更重要!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐