非常友好的Vue 3 生命周期详解

一、生命周期是什么?

简单理解:就像人的一生有"出生→成长→工作→退休"一样,Vue组件也有从创建到销毁的各个阶段,这些阶段就是"生命周期"。

二、Vue 3 生命周期全景图

text 复制代码
创建组件 → setup() → onBeforeMount → 渲染DOM → onMounted → 组件活跃
     ↓                                                      
更新数据 → onBeforeUpdate → 重新渲染 → onUpdated → 可能再次更新
     ↓
组件销毁 → onBeforeUnmount → 清理工作 → onUnmounted → 组件消失

三、8个核心生命周期详解(附代码例子)

1. setup() ​ - 组件的"出生前准备"

js 复制代码
// 这是Vue 3的新特性,是组合式API的入口
import { ref } from 'vue'

export default {
  setup() {
    console.log('1. setup - 最先执行,准备数据和函数')
    
    const count = ref(0)  // 定义响应式数据
    const add = () => { count.value++ }
    
    return { count, add }  // 返回给模板使用
  }
}

作用:初始化响应式数据、计算属性、方法等

最佳实践:所有组件的逻辑都应该从这里开始组织

2. onBeforeMount() ​ - 挂载前的最后检查

js 复制代码
import { onBeforeMount, ref } from 'vue'

export default {
  setup() {
    const message = ref('Hello')
    
    onBeforeMount(() => {
      console.log('2. 挂载前 - DOM还没生成,不能操作DOM')
      console.log('message:', message.value)  // 可以访问数据
      // document.getElementById('app')  // ❌ 这里获取不到DOM元素
    })
    
    return { message }
  }
}

作用:在组件挂载到DOM之前执行

最佳实践:可以在这里修改数据,但不能操作DOM

3. onMounted() ​ - 组件"上岗工作"

js 复制代码
import { onMounted, ref } from 'vue'

export default {
  setup() {
    const titleRef = ref(null)  // 模板引用
    
    onMounted(() => {
      console.log('3. 已挂载 - 组件已插入DOM,可以操作DOM了')
      
      // ✅ 可以获取DOM元素
      console.log('标题元素:', titleRef.value)
      
      // ✅ 可以调用第三方库
      // this.chart = echarts.init(this.$refs.chart)
      
      // ✅ 可以发送初始数据请求
      // fetchData()
    })
    
    return { titleRef }
  },
  template: '<h1 ref="titleRef">我是标题</h1>'
}

作用:组件已挂载到DOM,可以操作DOM、调用第三方库、发送网络请求

最佳实践

  • 在此处进行DOM操作
  • 发送初始化网络请求
  • 启动定时器
  • 绑定自定义事件

4. onBeforeUpdate() ​ - 更新前的准备

js 复制代码
import { onBeforeUpdate, ref } from 'vue'

export default {
  setup() {
    const count = ref(0)
    
    onBeforeUpdate(() => {
      console.log('4. 更新前 - 数据已变化,但页面还没重新渲染')
      console.log('当前count值:', count.value)
    })
    
    return { count }
  }
}

作用:响应式数据变化后,DOM重新渲染前

最佳实践:可以获取更新前的DOM状态

5. onUpdated() ​ - 更新完成

js 复制代码
import { onUpdated, ref } from 'vue'

export default {
  setup() {
    const count = ref(0)
    
    onUpdated(() => {
      console.log('5. 更新完成 - DOM已重新渲染')
      // console.log('新的DOM内容:', document.getElementById('counter').textContent)
    })
    
    return { count }
  },
  template: '<div id="counter">{{ count }}</div>'
}

重要提醒:避免在onUpdated中修改数据,可能导致无限循环!

最佳实践

  • 执行依赖于DOM更新的操作
  • 与父组件通信(通过$emit)

6. onBeforeUnmount() ​ - 退休前的交接

js 复制代码
import { onBeforeUnmount, ref } from 'vue'

export default {
  setup() {
    const timer = ref(null)
    
    onBeforeUnmount(() => {
      console.log('6. 卸载前 - 组件还在,但即将卸载')
      console.log('清理前状态:', document.contains(this.$el))
    })
    
    return {}
  }
}

作用:组件实例销毁前,此时组件功能完全正常

最佳实践:取消未完成的异步操作

7. onUnmounted() ​ - 组件"退休"

js 复制代码
import { onUnmounted, onMounted, ref } from 'vue'

export default {
  setup() {
    let timer = null
    
    onMounted(() => {
      console.log('组件已挂载')
      timer = setInterval(() => {
        console.log('计时器运行中...')
      }, 1000)
    })
    
    onUnmounted(() => {
      console.log('7. 已卸载 - 组件已销毁')
      clearInterval(timer)  // ✅ 必须清理!
      timer = null
    })
    
    return {}
  }
}

作用:组件销毁后执行清理工作

最佳实践(必须做!):

  • 清除定时器
  • 取消事件监听
  • 清理全局状态
  • 关闭WebSocket连接

销毁的实际场景

场景1:页面切换

js 复制代码
// 路由配置
const routes = [
  { path: '/home', component: Home },
  { path: '/about', component: About }
]

// 从 /home 切换到 /about 时:
// 1. Home 组件被销毁
// 2. About 组件被创建

场景2:条件渲染

html 复制代码
<template>
  <div>
    <button @click="show = !show">切换</button>
    
    <!-- v-if 为 false 时,Timer 组件被销毁 -->
    <Timer v-if="show" />
  </div>
</template>

场景3:列表项删除

html 复制代码
<template>
  <div v-for="item in list" :key="item.id">
    <ListItem :item="item" />
    <button @click="remove(item.id)">删除</button>
  </div>
</template>

<script setup>
// 点击删除时,对应的 ListItem 组件被销毁
</script>

8. onErrorCaptured() ​ - 错误处理

js 复制代码
import { onErrorCaptured } from 'vue'

export default {
  setup() {
    onErrorCaptured((error, instance, info) => {
      console.log('捕获到错误:', error)
      console.log('错误组件:', instance)
      console.log('错误信息:', info)
      
      // 可以上报错误到服务器
      // sendErrorToServer(error)
      
      return false  // 阻止错误继续向上传播
    })
    
    return {}
  }
}

作用:捕获子组件的错误

最佳实践:全局错误处理、错误上报

四、完整示例:一个计时器组件

html 复制代码
<template>
  <div>
    <h1>计时器: {{ count }} 秒</h1>
    <button @click="toggleTimer">{{ isRunning ? '暂停' : '开始' }}</button>
    <button @click="reset">重置</button>
  </div>
</template>

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

const count = ref(0)
const isRunning = ref(false)
let timer = null

// 1. 组件挂载时
onMounted(() => {
  console.log('组件已挂载,准备开始计时')
  startTimer()
})

// 2. 开始/暂停计时
const toggleTimer = () => {
  isRunning.value = !isRunning.value
  if (isRunning.value) {
    startTimer()
  } else {
    stopTimer()
  }
}

// 3. 启动计时器
const startTimer = () => {
  if (!timer) {
    timer = setInterval(() => {
      count.value++
      console.log(`计时: ${count.value}秒`)
    }, 1000)
  }
}

// 4. 停止计时器
const stopTimer = () => {
  if (timer) {
    clearInterval(timer)
    timer = null
  }
}

// 5. 重置
const reset = () => {
  count.value = 0
}

// 6. 组件销毁前
onBeforeUnmount(() => {
  console.log('组件即将销毁,准备清理...')
})

// 7. 组件销毁时(最重要!)
onUnmounted(() => {
  console.log('组件已销毁')
  stopTimer()  // 必须清理定时器!
})
</script>

五、生命周期最佳实践总结

生命周期 应该做什么 不应该做什么
setup 定义数据、方法、计算属性 操作DOM、发送请求
onMounted 操作DOM、发送请求、启动定时器 修改大量数据导致频繁更新
onUpdated 执行DOM更新后的操作 修改数据(可能导致无限循环)
onUnmounted 清理定时器、取消事件、关闭连接 调用组件方法(组件已不存在)

六、常见问题解答

Q1:created和beforeCreate去哪了?

A:在Vue 3的组合式API中,setup()替代了这两个钩子

Q2:onMounted和onUpdated哪个先执行?

A:首次加载时只执行onMounted,数据更新时才执行onUpdated

Q3:多个相同生命周期会按什么顺序执行?

A:按照代码书写的顺序执行

Q4:父组件和子组件的生命周期顺序?

A:

markdown 复制代码
父beforeCreate → 父created → 父beforeMount
    ↓
子beforeCreate → 子created → 子beforeMount → 子mounted
    ↓
父mounted

七、一句话记住每个生命周期

  1. setup - 出生前,准备东西
  2. onBeforeMount - 要上岗了,最后检查
  3. onMounted - 已上岗,开始工作
  4. onBeforeUpdate - 要改方案,先想想
  5. onUpdated - 方案已改,完成
  6. onBeforeUnmount - 要退休了,交接工作
  7. onUnmounted - 已退休,清理办公桌
  8. onErrorCaptured - 下属犯错,我来处理
相关推荐
AAA阿giao1 小时前
从零构建一个现代登录页:深入解析 Tailwind CSS + Vite + Lucide React 的完整技术栈
前端·css·react.js
兆子龙2 小时前
像 React Hook 一样「自动触发」:用 Git Hook 拦住忘删的测试代码与其它翻车现场
前端·架构
兆子龙3 小时前
用 Auto.js 实现挂机脚本:从找图点击到循环自动化
前端·架构
SuperEugene3 小时前
表单最佳实践:从 v-model 到自定义表单组件(含校验)
前端·javascript·vue.js
昨晚我输给了一辆AE863 小时前
为什么现在不推荐使用 React.FC 了?
前端·react.js·typescript
不会敲代码13 小时前
深入浅出 React 闭包陷阱:从现象到原理
前端·react.js
不会敲代码13 小时前
React性能优化:深入理解useMemo和useCallback
前端·javascript·react.js
Dilettante2583 小时前
我的 Monorepo 实践经验:从基础概念到最佳实践
前端·前端工程化
只会cv的前端攻城狮3 小时前
Elpis-Core — 融合 Koa 洋葱圈模型实现服务端引擎
前端·后端