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 事件配置
通俗理解:注册“监听器”和“回调函数”。
这指的是你在组件里写的:
watch:监听某个数据变化,执行特定逻辑。- 自定义事件:组件间通信的
$emit/$on(Vue 2) 或defineEmits(Vue 3)。 - 方法绑定:把
methods里的函数绑定到实例上。
这就是setup()的优势了,在Vue2中,beforeCreate是Event/Watcher事件配置之前调用的,会出现调用beforeCreate实例的时候,压根没有watch,methods等方法,出现bug
setup()函数的执行时机,大致相当于beforeCreate+created的结合体。- 你在
setup里定义的ref或reactive,定义即观测,不需要等待后续步骤。 - 你在
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. 周期钩子的写法说明
- 直接在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→初始化选项式 API→created
- 渲染器遇到组件:Vue 开始在页面上处理这个组件。
- Setup / BeforeCreate:
setup()(组合式 API):这是 Vue 3 的入口点,最早执行。在这里定义响应式数据和方法。beforeCreate(选项式 API):在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。- 注意:在 Vue 3 中,通常只用
setup,它涵盖了beforeCreate和created的功能。
- 初始化选项式 API:如果你使用了传统的
data,methods等写法,这里会进行初始化。 - Created:
-
created:实例已经创建完成。此时数据观测、属性和方法的运算、watch/event 事件回调都已完成。但是,此时还没有挂载到页面上(DOM 还不存在),所以不能操作 DOM 元素。
3.2. 阶段二:编译与挂载
流程: 判断模板 → beforeMount → 初始渲染 (创建 DOM) → mounted
- 是否存在预编译模板?
-
- NO:如果是运行时编译(比如在浏览器里直接写 template),需要先即时编译模板。
- YES:如果是构建工具(如 Vite/Webpack)预编译好的 render 函数,直接使用。
- BeforeMount:
-
beforeMount:在挂载开始之前被调用。相关的 render 函数首次被调用。此时页面还是旧的(或者空的)。
- 初始渲染 (创建和插入 DOM 节点) :Vue 根据模板生成真实的 HTML 元素,并插入到页面中。
- Mounted:
-
mounted:关键节点! 组件已经挂载到页面上,DOM 已经生成。- 应用场景:如果你需要操作 DOM(比如初始化图表、获取元素宽高、发起网络请求),通常放在这里(或者
onMounted)。
3.3. 阶段三:更新循环
流程: 数据变化 → beforeUpdate → 重新渲染并打补丁 → updated
这是一个循环过程,只要组件里的响应式数据发生变化,就会触发:
- 当数据变化时:你修改了
ref或reactive中的数据。 - BeforeUpdate:
-
beforeUpdate:数据更新时调用,发生在虚拟 DOM 打补丁之前。此时你可以访问到更新前的 DOM 状态。
- 重新渲染并打补丁:Vue 对比新旧虚拟 DOM,计算出最小的差异(Diff 算法),然后高效地更新真实 DOM。
- Updated:
-
updated:由于数据更改导致的虚拟 DOM 重新渲染和打补丁之后调用。此时 DOM 已经是更新后的状态了。- 注意:尽量避免在
updated中修改数据,否则可能导致无限循环更新。
3.4. 阶段四:卸载/销毁
流程: 组件被取消挂载 → beforeUnmount → 取消挂载 → unmounted
- 当组件被取消挂载时。
- BeforeUnmount (Vue 3) /
beforeDestroy(Vue 2):
-
beforeUnmount:在卸载组件实例之前调用。此时实例还完全可用。- 应用场景:清除定时器、解绑全局事件监听器、断开 WebSocket 连接等清理工作。
- 取消挂载:Vue 移除组件对应的 DOM 元素。
- 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 | 彻底清理 |