极米投影仪
149.20M · 2026-03-22
墨渊书肆/Vue 底层原理 & 新特性
Vue 自发布以来经历了多个重要版本的迭代,每个版本的改动都带来了架构优化和新特性,同时也伴随着一些 Breaking Changes。以下是 Vue 各个重要版本的变动概述:
Vue2 正式引入了虚拟 DOM,这是框架性能提升的关键技术。Vue2 最后的大版本,引入了一些 Composition API 的向下兼容实现,为 Vue3 迁移做铺垫。Vue2 响应式的诸多痛点。Vue 团队正在实验的全新渲染策略,跳过虚拟 DOM直接生成高效的 JavaScript 代码。响应式系统是 Vue 的核心,也是面试中的高频考点。Vue2 和 Vue3 在响应式实现上有着本质的区别。
Vue2 使用 Object.defineProperty 来劫持数据的 getter 和 setter:
function defineReactive(obj, key, val) {
// 为每个属性创建 Dep 实例
const dep = new Dep()
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
// 依赖收集
if (Dep.target) {
dep.depend()
}
return val
},
set: function reactiveSetter(newVal) {
if (newVal === val) return
// 通知更新
dep.notify()
}
})
}
Vue2 响应式的局限性:
Object.defineProperty 只能劫持已存在的属性,对于新增属性无能为力。arr[0] = value 不会触发更新。解决方案:Vue2 提供了 Vue.set / Vue.delete 以及重写数组方法来应对这些场景。
Vue3 使用 ES6 的 Proxy 来实现响应式:
function reactive(target) {
return new Proxy(target, {
get(target, key, receiver) {
// 依赖收集
track(target, key)
const result = Reflect.get(target, key, receiver)
// 如果是对象,递归代理实现深层响应式
return isObject(result) ? reactive(result) : result
},
set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver)
// 触发更新
trigger(target, key)
return result
},
deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key)
trigger(target, key)
return result
}
})
}
Vue3 响应式的优势:
Vue 的响应式系统遵循观察者模式,包含三个核心角色:
// Dep 实现
class Dep {
constructor() {
this.subs = new Set() // 存储 Watcher
}
depend() {
if (Dep.target) {
this.subs.add(Dep.target)
}
}
notify() {
this.subs.forEach(watcher => watcher.update())
}
}
// Watcher 实现
class Watcher {
constructor(fn) {
this.getter = fn
this.value = this.get()
}
get() {
Dep.target = this
const value = this.getter()
Dep.target = null
return value
}
update() {
this.value = this.getter()
}
}
Vue 的模板编译是将模板字符串转换为可执行渲染函数的过程,主要分为三个阶段。
将模板字符串解析为 AST(抽象语法树):
// 模板
<div class="container">
<h1>{{ title }}</h1>
</div>
// AST 结构
{
type: 'Element',
tag: 'div',
props: [{ type: 'Attribute', name: 'class', value: 'container' }],
children: [
{
type: 'Element',
tag: 'h1',
children: [{ type: 'Interpolation', content: { expression: 'title' } }]
}
]
}
Vue3 的编译器会进行静态节点提升(Static Hoisting):
// 优化前
render() {
return h('button', { onClick: this.handleClick }, 'Click')
}
// 优化后 - 事件函数被缓存
const handleClick = this.handleClick
render() {
return h('button', { onClick: handleClick }, 'Click')
}
将 AST 转换为渲染函数:
// 生成的渲染函数
function render() {
return _vue.createVNode('div', { class: 'container' }, [
_vue.createVNode('h1', null, _vue.toDisplayString(this.title))
])
}
虚拟 DOM 是真实 DOM 的 JavaScript 对象表示:
// VNode 结构
const vnode = {
type: 'div',
props: { class: 'container' },
children: [
{ type: 'h1', children: 'Hello' }
],
el: null // 关联的真实 DOM 引用
}
虚拟 DOM 的优势:
Vue2 采用传统的 Diff 算法,从左到右依次对比:
function updateChildren(oldChildren, newChildren) {
let oldStartIndex = 0
let newStartIndex = 0
let oldEndIndex = oldChildren.length - 1
let newEndIndex = newChildren.length - 1
while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {
// 简单比较...O(n) 复杂度
}
}
Vue3 采用了更高效的 Diff 算法:
// Vue3 Diff 核心逻辑
function diffChildren(n1, n2, parent) {
const c1 = n1.children
const c2 = n2.children
const oldStart = 0
const newStart = 0
const oldEnd = c1.length - 1
const newEnd = c2.length - 1
// 双端比较策略
while (oldStart <= oldEnd && newStart <= newEnd) {
if (c1[oldStart].key === c2[newStart].key) {
// 节点相同,继续
patch(c1[oldStart], c2[newStart], parent)
oldStart++
newStart++
} else if (c1[oldEnd].key === c2[newEnd].key) {
// 尾部匹配
patch(c1[oldEnd], c2[newEnd], parent)
oldEnd--
newEnd--
}
// ... 更多比较策略
}
}
| 阶段 | 钩子 | 说明 |
|---|---|---|
| 初始化 | beforeCreate | 实例刚创建,数据观测未完成 |
| 初始化 | created | 数据观测完成,DOM 未生成 |
| 挂载 | beforeMount | 模板编译完成,准备挂载 |
| 挂载 | mounted | DOM 挂载完成,可操作 DOM |
| 更新 | beforeUpdate | 数据变化,DOM 未更新 |
| 更新 | updated | DOM 更新完成 |
| 销毁 | beforeDestroy | 实例销毁前,可清理 |
| 销毁 | destroyed | 实例已销毁 |
| Vue2 | Vue3 (Composition API) |
|---|---|
beforeCreate | - |
created | - |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeDestroy | onBeforeUnmount |
destroyed | onUnmounted |
数据变化 → 触发 setter → Dep 通知 Watcher →
触发 update() → 重新执行 render() 生成新的 VNode →
Diff 对比 → 更新真实 DOM
组合式 API 是 Vue3 最重要的变化,提供了更灵活的逻辑组织方式:
// setup 函数 - 组件逻辑入口
import { ref, computed, onMounted, watch } from 'vue'
export default {
setup() {
const count = ref(0)
const doubled = computed(() => count.value * 2)
function increment() {
count.value++
}
onMounted(() => {
console.log('Component mounted!')
})
watch(count, (newVal) => {
console.log(`Count changed to ${newVal}`)
})
return { count, doubled, increment }
}
}
ref vs reactive:
ref:用于原始类型,创建包含 .value 的响应式对象。reactive:用于对象,创建深层响应式对象。import { ref, reactive } from 'vue'
const count = ref(0) // 原始类型
const state = reactive({ // 对象类型
user: { name: 'Vue' }
})
// 模板中自动解包
console.log(count.value) // JS 中需要 .value
console.log(state.user) // reactive 直接访问
将组件渲染到指定 DOM 位置,常用于模态框:
<Teleport to="body">
<div v-if="show" class="modal">
<p>Modal Content</p>
</div>
</Teleport>
支持多根节点模板:
<!-- Vue3 允许 -->
<template>
<div>A</div>
<div>B</div>
</template>
处理异步组件加载状态:
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
Loading...
</template>
</Suspense>
| 特性 | Vue2 | Vue3 | React |
|---|---|---|---|
| 原理 | Object.defineProperty | Proxy | useState/useReducer |
| 触发方式 | 自动 | 自动 | 手动调用 setState |
| 数组响应 | 重写方法 | Proxy | 需使用 Immer 或 immutable |
| 深层 | 递归 | Proxy 懒加载 | useEffect 依赖 |
Vue3 由于模板编译优化和 Proxy 响应式,在大多数场景下性能优于 React。React 的优势在于 Fiber 架构带来的精细化控制和并发渲染能力。
// 使用 v-once 静态内容
<div v-once>{{ staticContent }}</div>
// 正确使用 key
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
// v-if vs v-show 选择
<div v-if="show">很少切换</div>
<div v-show="show">频繁切换</div>
import { shallowRef, markRaw } from 'vue'
// 浅层响应式 - 适合大型数据
const largeList = shallowRef([])
// 非响应式数据 - 适合不需要响应式的对象
const plainObj = markRaw({ /* ... */ })
// 路由懒加载
const Home = () => import('./views/Home.vue')
// 异步组件
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() => import('./AsyncComp.vue'))
<KeepAlive include="Home,About">
<router-view />
</KeepAlive>
Vue2 使用 Object.defineProperty,需要递归所有属性,无法检测新增/删除属性;Vue3 使用 Proxy,原生支持属性增删,性能更好。
通过 Dep 类管理订阅者,Watcher 在读取响应式属性时将自身添加到 Dep,属性变化时 Dep 通知所有 Watcher 更新。
Vue3 采用 双端比较 策略,结合 最长递增子序列 算法,最小化 DOM 移动次数,复杂度从 O(n³) 优化到 O(n)。
Vue 使用 Promise + MutationObserver + setTimeout 实现异步队列,在 DOM 更新后通过微任务执行回调。
通过缓存 VNode,保存组件实例和状态,切换时复用而非重新创建。activated/deactivated 钩子用于感知缓存状态变化。
解析 → 优化(静态节点提升)→ 代码生成(渲染函数)
Vue 作为一个渐进式框架,在保持易用性的同时不断深化底层技术的实现。Vue3 通过 Composition API、Proxy 响应式系统、优化的 Diff 算法等特性,显著提升了开发体验和运行性能。理解这些底层原理不仅有助于应对面试,更能在实际开发中做出更好的技术决策。
Vue 团队正在探索的 Vapor Mode 未来可能带来更大的性能突破,值得持续关注。