阅读视图

发现新文章,点击刷新页面。

【从零开始学习Vue|第六篇】生命周期

1. 前置知识

a. 数据观测(Data Observer)

  • 在 Vue 2 中,这叫 Object.defineProperty;在 Vue 3 中,这叫 Proxy
  • 它的作用是:当你修改数据时,Vue 能立刻知道,并自动更新页面。

比如说没有输据观测时

let data = { count: 0 };
data.count = 1; 
// 👉 Vue 完全不知道 count 变了,页面也不会更新。
// 就像你把东西藏起来了,没人看见。

有数据观测时:Vue 会把你的对象包裹一层,变成这样(简化版):

let data = new Proxy({ count: 0 }, {
  set(target, key, value) {
    console.log(`嘿!有人把 ${key}${target[key]} 改成了 ${value}`);
    target[key] = value;
    triggerUpdate(); // 🔔 触发页面更新!
    return true;
  }
});

data.count = 1; 
// 👉 控制台打印:"嘿!有人把 count 从 0 改成了 1"
// 👉 页面自动刷新显示 1。

b. Event/Watcher 事件配置

通俗理解:注册“监听器”和“回调函数”。

这指的是你在组件里写的:

  1. watch:监听某个数据变化,执行特定逻辑。
  2. 自定义事件:组件间通信的 $emit / $on (Vue 2) 或 defineEmits (Vue 3)。
  3. 方法绑定:把 methods 里的函数绑定到实例上。

这就是setup()的优势了,在Vue2中,beforeCreate是Event/Watcher事件配置之前调用的,会出现调用beforeCreate实例的时候,压根没有watch,methods等方法,出现bug

  • setup() 函数的执行时机,大致相当于 beforeCreate + created 的结合体。
  • 你在 setup 里定义的 refreactive定义即观测,不需要等待后续步骤。
  • 你在 setup 里写的逻辑,天然就能访问到数据。避免了bug的出现

c. 关于vue的模版编译:预编译和即时编译

ⅰ. ****模板编译是什么?

Vue 写的模板(template)浏览器是看不懂的,需要转换成 JavaScript 的 渲染函数(render function) 才能执行。

template (你写的)  →  [编译]  →  render function (浏览器能执行)

这个编译过程可以在两个时间点进行:

ⅱ. 预编译模版(Pre-compiled)

在打包构建阶段(开发时),就把 template 转换成 render function,浏览器拿到的是已经编译好的代码。

这其实就是用了vite/webpack构建工具编译的

开发阶段 (你的电脑)              用户浏览器
     ↓                              ↓
写 template  →  构建工具编译  →  下发 render 函数
              (Vite/Webpack)

ⅲ. 即时编译模版(Reunime Compilation)

在浏览器运行时,Vue 拿到 template 字符串后,现场编译成 render function 再执行。

这其实就是引入CDN的方式

开发阶段 (你的电脑)              用户浏览器
     ↓                              ↓
写 template  →  直接下发 template  →  浏览器现场编译 →  执行
                                    (Vue 编译器)

ⅳ. 区别

特性 预编译 即时编译
编译时间 开发/构建时 浏览器运行时
需要构建工具 ✅ 需要 (Vite/Webpack) ❌ 不需要
运行时性能 🚀 快 🐌 慢
包体积 📦 小 (不含编译器) 📦 大 (含编译器)
动态模板 ❌ 不支持 ✅ 支持
使用场景 大多数项目 CDN/在线编辑器/低代码

2. 基础说明以及如何注册周期钩子

每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到 DOM,以及在数据改变时更新 DOM。在此过程中,它也会运行被称为生命周期钩子的函数,让开发者有机会在特定阶段运行自己的代码。

2.1. 注册周期钩子

举例来说,onMounted 钩子可以用来在组件完成初始渲染并创建 DOM 节点后运行代码:

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

onMounted(() => {
  console.log(`the component is now mounted.`)
})
</script>

还有其他一些钩子,会在实例生命周期的不同阶段被调用。

当调用 onMounted 时,Vue 会自动将回调函数注册到当前正被初始化的组件实例上。这意味着这些钩子应当在组件初始化时被同步注册。

onMounted() 不一定要直接写在 <script setup> 的最外层,可以封装到函数里调用,只要这个函数是在 setup同步调用的就行。

2.2. 周期钩子的写法说明

  1. 直接在setup中调用
<script setup>
import { onMounted } from 'vue'

// 直接写在 setup 顶层
onMounted(() => {
  console.log('组件已挂载')
})
</script>

2. 在外部函数中调用

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

// 定义一个外部函数
function registerHooks() {
  onMounted(() => {
    console.log('组件已挂载')
  })
}

// 在 setup 中同步调用这个函数
registerHooks()
</script>

3. 封装成自定义HooK

<!-- hooks/useMountLog.js -->
import { onMounted } from 'vue'

export function useMountLog(message) {
  onMounted(() => {
    console.log(message)
  })
}

<!-- 组件中使用 -->
<script setup>
import { useMountLog } from './hooks/useMountLog'

// 调用自定义 Hook
useMountLog('用户组件已挂载')
</script>

4. 错误写法(异步调用)

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

// 错误:异步调用,调用栈断了
setTimeout(() => {
  onMounted(() => {
    console.log('这不会工作')
  })
}, 1000)

// 错误:在 Promise 回调中调用
someAsyncFunction().then(() => {
  onMounted(() => {
    console.log('这也不会工作')
  })
})
</script>

3. 生命周期图示

3.1. 阶段一:初始化与创建

  • 流程: 渲染器遇到组件setup / beforeCreate初始化选项式 APIcreated

  1. 渲染器遇到组件:Vue 开始在页面上处理这个组件。
  2. Setup / BeforeCreate
  • setup() (组合式 API):这是 Vue 3 的入口点,最早执行。在这里定义响应式数据和方法。
  • beforeCreate (选项式 API):在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
  • 注意:在 Vue 3 中,通常只用 setup ,它涵盖了 beforeCreate created 的功能。
  1. 初始化选项式 API:如果你使用了传统的 data, methods 等写法,这里会进行初始化。
  2. Created
    • created:实例已经创建完成。此时数据观测、属性和方法的运算、watch/event 事件回调都已完成。但是,此时还没有挂载到页面上(DOM 还不存在),所以不能操作 DOM 元素。

3.2. 阶段二:编译与挂载

流程: 判断模板beforeMount初始渲染 (创建 DOM)mounted

  1. 是否存在预编译模板?
    • NO:如果是运行时编译(比如在浏览器里直接写 template),需要先即时编译模板
    • YES:如果是构建工具(如 Vite/Webpack)预编译好的 render 函数,直接使用。
  1. BeforeMount
    • beforeMount:在挂载开始之前被调用。相关的 render 函数首次被调用。此时页面还是旧的(或者空的)。
  1. 初始渲染 (创建和插入 DOM 节点) :Vue 根据模板生成真实的 HTML 元素,并插入到页面中。
  2. Mounted
    • mounted关键节点! 组件已经挂载到页面上,DOM 已经生成
    • 应用场景:如果你需要操作 DOM(比如初始化图表、获取元素宽高、发起网络请求),通常放在这里(或者 onMounted)。

3.3. 阶段三:更新循环

流程: 数据变化beforeUpdate重新渲染并打补丁updated

这是一个循环过程,只要组件里的响应式数据发生变化,就会触发:

  1. 当数据变化时:你修改了 refreactive 中的数据。
  2. BeforeUpdate
    • beforeUpdate:数据更新时调用,发生在虚拟 DOM 打补丁之前。此时你可以访问到更新前的 DOM 状态。
  1. 重新渲染并打补丁:Vue 对比新旧虚拟 DOM,计算出最小的差异(Diff 算法),然后高效地更新真实 DOM。
  2. Updated
    • updated:由于数据更改导致的虚拟 DOM 重新渲染和打补丁之后调用。此时 DOM 已经是更新后的状态了。
    • 注意:尽量避免在 updated 中修改数据,否则可能导致无限循环更新。

3.4. 阶段四:卸载/销毁

流程: 组件被取消挂载beforeUnmount取消挂载unmounted

  1. 当组件被取消挂载时
  2. BeforeUnmount (Vue 3) / beforeDestroy (Vue 2):
    • beforeUnmount:在卸载组件实例之前调用。此时实例还完全可用。
    • 应用场景:清除定时器、解绑全局事件监听器、断开 WebSocket 连接等清理工作。
  1. 取消挂载:Vue 移除组件对应的 DOM 元素。
  2. Unmounted (Vue 3) / destroyed (Vue 2):
    • unmounted:组件已卸载。所有指令都被解绑,所有事件监听器被移除,所有子实例也被销毁。

3.5. 选项式API 组合式API 生命周期区别

选项式 API (图中红色框) 组合式 API (<script setup>) 最佳使用时机
beforeCreate / created setup() 初始化数据、方法
beforeMount onBeforeMount 很少用到
mounted onMounted 操作 DOM、发请求 (最常用)
beforeUpdate onBeforeUpdate 获取更新前的 DOM
updated onUpdated 获取更新后的 DOM
beforeUnmount onBeforeUnmount 清理副作用 (定时器/监听器)
unmounted onUnmounted 彻底清理
❌