第4章 Vue3生命周期:生命周期钩子函数详解与实战应用

生命周期,说白了就是组件从生到死的全过程。Vue3 把这一套流程分成了几个关键节点,每个节点都给你留了一个“钩子”,让你能插进去干点私活。我个人习惯把生命周期理解成“组件的成长日记”——什么时候出生、什么时候长身体、什么时候干活、什么时候消亡,清清楚楚。

4.1 生命周期整体流程

先看一张总图,心里有个谱。Vue3 的生命周期主要分三个阶段:

  • 创建阶段:组件实例被创建,数据初始化
  • 挂载阶段:组件渲染到 DOM 上
  • 卸载阶段:组件从 DOM 上移除

嗯,这里要注意,Vue3 的 Composition API 里,生命周期钩子都改成了 onXxx 的形式。和 Vue2 的 createdmounted 写法不一样了。你想想看,为什么改?说白了是为了更好的逻辑复用和类型推导。

4.2 setup 函数——一切开始的地方

setup 是 Composition API 的入口。它在组件创建之前执行,比 beforeCreate 还早。我在项目中遇到过不少新手把 setup 当成 created 来用,其实不完全对。

import { ref, onMounted } from 'vue'

export default {
  setup() {
    const count = ref(0)
    console.log('setup 执行了,此时组件还没挂载')

    onMounted(() => {
      console.log('组件挂载完成')
    })

    return { count }
  }
}

核心要点setup 中不能访问 this,因为组件实例还没完全创建好。所有响应式数据、方法、生命周期钩子都在这里定义。

4.3 常用的生命周期钩子

Vue3 提供了 8 个主要钩子,我挑几个最常用的给你讲讲。

钩子函数 触发时机 常见用途
onBeforeMount 组件挂载到 DOM 之前 最后一次修改数据的机会
onMounted 组件挂载完成后 发起 API 请求、操作 DOM
onBeforeUpdate 数据更新、DOM 重新渲染之前 获取更新前的 DOM 状态
onUpdated 数据更新、DOM 重新渲染之后 执行依赖 DOM 的操作
onBeforeUnmount 组件卸载之前 清理定时器、取消订阅
onUnmounted 组件卸载之后 收尾工作

4.4 onMounted——最常用的钩子

我个人觉得 onMounted 是使用频率最高的钩子。为什么?因为这时候 DOM 已经渲染好了,你可以放心地操作它。

import { onMounted, ref } from 'vue'

export default {
  setup() {
    const userList = ref([])

    onMounted(async () => {
      // 发起数据请求
      const res = await fetch('/api/users')
      userList.value = await res.json()
    })

    return { userList }
  }
}

小技巧onMounted 里不要做太重的同步操作,否则会阻塞渲染。异步请求放在这里是最合适的。

4.5 onUnmounted——别忘了清理现场

我曾经在项目中犯过一个低级错误:在 onMounted 里开了个定时器,结果组件卸载了定时器还在跑。页面切走了,控制台还在疯狂输出日志。嗯,从那以后我养成了习惯——所有副作用都要在 onUnmounted 里清理干净。

import { onMounted, onUnmounted, ref } from 'vue'

export default {
  setup() {
    const timer = ref(null)

    onMounted(() => {
      timer.value = setInterval(() => {
        console.log('心跳检测中...')
      }, 1000)
    })

    onUnmounted(() => {
      if (timer.value) {
        clearInterval(timer.value)
        timer.value = null
      }
    })
  }
}

避坑指南:我曾经见过有人把 addEventListener 加在 onMounted 里,但忘了在 onUnmountedremoveEventListener。结果就是内存泄漏,页面越用越卡。记住:谁污染谁治理。

4.6 onBeforeUnmount——最后的抢救机会

这个钩子触发时,组件还在 DOM 上,但马上就要被移除了。你可以在这里做一些“临终关怀”。比如保存草稿、记录用户行为等。

import { onBeforeUnmount } from 'vue'

export default {
  setup() {
    onBeforeUnmount(() => {
      // 保存用户未提交的表单数据
      localStorage.setItem('draft', JSON.stringify(formData))
      console.log('草稿已保存')
    })
  }
}

4.7 生命周期实战——一个完整的例子

光说不练假把式。我们写一个真实场景:一个实时数据监控面板。组件挂载时开始监听,卸载时停止监听。

import { ref, onMounted, onUnmounted } from 'vue'

export default {
  setup() {
    const cpuUsage = ref(0)
    const memoryUsage = ref(0)
    let ws = null

    const connectWebSocket = () => {
      ws = new WebSocket('ws://monitor.example.com')
      ws.onmessage = (event) => {
        const data = JSON.parse(event.data)
        cpuUsage.value = data.cpu
        memoryUsage.value = data.memory
      }
    }

    const disconnectWebSocket = () => {
      if (ws) {
        ws.close()
        ws = null
      }
    }

    onMounted(() => {
      connectWebSocket()
    })

    onUnmounted(() => {
      disconnectWebSocket()
    })

    return { cpuUsage, memoryUsage }
  }
}

实战要点:WebSocket 连接一定要在 onUnmounted 里关闭。否则页面切走了,连接还在,服务器还在给你推送数据,白白浪费带宽。

4.8 生命周期执行顺序

如果你在父组件里嵌套了子组件,生命周期的执行顺序是这样的:

  1. 父组件 setup
  2. 父组件 onBeforeMount
  3. 子组件 setup
  4. 子组件 onBeforeMount
  5. 子组件 onMounted
  6. 父组件 onMounted

为什么会这样?因为父组件要等所有子组件都挂载完了,自己才算挂载完成。这个顺序在写复杂组件时特别重要。我记得有一次调试一个弹窗组件,子组件的数据一直拿不到,后来才发现是生命周期顺序搞反了。

4.9 总结

生命周期是 Vue 组件的骨架。掌握了它,你就知道什么时候该做什么事。我个人建议:

  • 数据请求放 onMounted
  • 副作用清理放 onUnmounted
  • DOM 操作放 onMountedonUpdated
  • 初始化逻辑放 setup

记住一句话:谁创建谁销毁,谁订阅谁取消。做到这四点,生命周期这块基本就稳了。

下一章我们会讲 Vue3 的响应式原理,看看 refreactive 到底是怎么工作的。到时候你会发现,理解了生命周期,响应式就更好理解了。