摘要
Vue3 的响应式系统是其核心特性,它通过 Proxy 和 Reflect API 实现了更强大、更高效的依赖追踪。本文将深入探讨 Vue3 响应式系统的工作原理、依赖收集、触发更新等核心机制,通过详细的代码示例、执行流程分析和性能对比,帮助你彻底掌握 Vue3 响应式系统的完整知识体系。
一、 Vue3 响应式系统概述
1.1 Vue2 响应式系统的局限性
Vue2 使用 Object.defineProperty 实现响应式:
// Vue2 响应式实现简例
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
console.log(`读取 ${key}: ${val}`)
return val
},
set(newVal) {
if (newVal !== val) {
console.log(`设置 ${key}: ${newVal}`)
val = newVal
}
}
})
}
const data = { count: 0 }
defineReactive(data, 'count', data.count)
Vue2 响应式的痛点:
- 无法检测属性添加/删除:需要使用
Vue.set/Vue.delete - 数组方法需要重写:通过修改数组原型方法实现响应式
- 性能开销大:需要递归遍历所有属性
- 内存占用高:每个属性都需要单独的 getter/setter
1.2 Vue3 响应式系统的优势
Vue3 使用 Proxy 实现响应式:
// Vue3 响应式实现简例
function reactive(obj) {
return new Proxy(obj, {
get(target, key, receiver) {
console.log(`读取 ${String(key)}`)
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
console.log(`设置 ${String(key)}: ${value}`)
return Reflect.set(target, key, value, receiver)
}
})
}
const data = reactive({ count: 0 })
data.count = 1 // 触发 set
Vue3 响应式的优势:
- 全面拦截:可以拦截所有操作(增、删、改、查)
- 更好的性能:惰性代理,按需响应
- 更好的 TypeScript 支持:完整的类型推断
- 更小的内存占用:不需要预先定义所有属性
二、 Vue3 响应式系统核心架构
2.1 响应式系统整体架构
流程图:Vue3 响应式系统完整工作流程
flowchart TD
A[创建响应式对象] --> B[Proxy 拦截操作]
B --> C{操作类型}
C --> D[GET 操作]
D --> E[追踪依赖<br>track]
E --> F[记录effect-target映射]
C --> G[SET 操作]
G --> H[触发更新<br>trigger]
H --> I[查找对应effect]
I --> J[执行副作用函数]
C --> K[其他操作<br>has/delete等]
K --> L[相应处理]
J --> M[组件重新渲染]
M --> N[DOM 更新]
2.2 核心概念解析
- Target:被代理的原始对象
- Proxy:代理对象,拦截对 Target 的操作
- Effect:副作用函数,当依赖变化时需要重新执行
- Track:依赖收集,建立属性与 Effect 的映射关系
- Trigger:派发更新,当属性变化时通知相关 Effect
三、 响应式系统核心实现原理
3.1 基础响应式实现
让我们从零开始实现一个简化的 Vue3 响应式系统:
// 简化版 Vue3 响应式系统实现
class ReactiveSystem {
constructor() {
this.targetMap = new WeakMap() // 存储目标对象到依赖的映射
this.activeEffect = null // 当前活动的副作用函数
}
// 依赖收集
track(target, key) {
if (!this.activeEffect) return
let depsMap = this.targetMap.get(target)
if (!depsMap) {
depsMap = new Map()
this.targetMap.set(target, depsMap)
}
let dep = depsMap.get(key)
if (!dep) {
dep = new Set()
depsMap.set(key, dep)
}
dep.add(this.activeEffect)
console.log(`📌 追踪依赖: ${key} ->`, this.activeEffect.name)
}
// 派发更新
trigger(target, key) {
const depsMap = this.targetMap.get(target)
if (!depsMap) return
const dep = depsMap.get(key)
if (dep) {
console.log(`🚀 触发更新: ${key},影响 ${dep.size} 个副作用`)
dep.forEach(effect => {
if (effect.scheduler) {
effect.scheduler()
} else {
effect()
}
})
}
}
// 创建响应式对象
reactive(obj) {
const handler = {
get(target, key, receiver) {
console.log(`🔍 读取属性: ${String(key)}`)
const result = Reflect.get(target, key, receiver)
// 追踪依赖
this.track(target, key)
// 如果结果是对象,递归代理
if (result !== null && typeof result === 'object') {
return this.reactive(result)
}
return result
}.bind(this),
set(target, key, value, receiver) {
console.log(`✏️ 设置属性: ${String(key)} = ${value}`)
const oldValue = target[key]
const result = Reflect.set(target, key, value, receiver)
// 只有值真正改变时才触发更新
if (oldValue !== value) {
this.trigger(target, key)
}
return result
}.bind(this),
deleteProperty(target, key) {
console.log(`🗑️ 删除属性: ${String(key)}`)
const hadKey = Object.prototype.hasOwnProperty.call(target, key)
const result = Reflect.deleteProperty(target, key)
if (hadKey && result) {
this.trigger(target, key)
}
return result
}.bind(this)
}
return new Proxy(obj, handler)
}
// 创建副作用函数
effect(fn, options = {}) {
const effectFn = () => {
this.activeEffect = effectFn
const result = fn()
this.activeEffect = null
return result
}
effectFn.name = fn.name || 'anonymous'
effectFn.scheduler = options.scheduler
// 立即执行一次
effectFn()
return effectFn
}
}
// 使用示例
const system = new ReactiveSystem()
// 创建响应式对象
const state = system.reactive({
count: 0,
user: {
name: '张三',
age: 25
}
})
// 创建副作用函数
system.effect(function render() {
console.log(`🎨 渲染: count = ${state.count}, name = ${state.user.name}`)
document.getElementById('app').innerHTML = `
<div>
<h2>计数: ${state.count}</h2>
<p>用户: ${state.user.name} (${state.user.age}岁)</p>
</div>
`
})
// 测试响应式
setTimeout(() => {
state.count = 1 // 触发更新
}, 1000)
setTimeout(() => {
state.user.name = '李四' // 触发更新
}, 2000)
setTimeout(() => {
state.user.age = 26 // 触发更新
}, 3000)
3.2 完整的响应式系统演示
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue3 响应式系统演示</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1000px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
}
.demo-container {
background: white;
border-radius: 12px;
padding: 30px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
margin-bottom: 30px;
}
.controls {
display: flex;
gap: 15px;
margin: 20px 0;
flex-wrap: wrap;
}
button {
padding: 10px 20px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s;
}
.btn-primary { background: #42b883; color: white; }
.btn-secondary { background: #3498db; color: white; }
.btn-warning { background: #e67e22; color: white; }
.btn-danger { background: #e74c3c; color: white; }
button:hover { opacity: 0.9; transform: translateY(-1px); }
.log-container {
background: #2c3e50;
color: white;
border-radius: 8px;
padding: 20px;
margin-top: 20px;
max-height: 400px;
overflow-y: auto;
font-family: 'Courier New', monospace;
font-size: 12px;
}
.log-item {
padding: 8px 12px;
margin: 4px 0;
background: #34495e;
border-radius: 4px;
border-left: 4px solid #42b883;
}
.data-display {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 20px 0;
}
.data-card {
background: #f8f9fa;
padding: 15px;
border-radius: 8px;
border: 1px solid #e9ecef;
}
.data-card h4 {
margin: 0 0 10px 0;
color: #2c3e50;
}
</style>
</head>
<body>
<div class="demo-container">
<h1>Vue3 响应式系统深度演示</h1>
<div class="data-display">
<div class="data-card">
<h4>基本数据</h4>
<div id="basic-data">计数: 0, 消息: Hello</div>
</div>
<div class="data-card">
<h4>用户信息</h4>
<div id="user-data">姓名: 张三, 年龄: 25</div>
</div>
<div class="data-card">
<h4>计算属性</h4>
<div id="computed-data">双倍计数: 0</div>
</div>
<div class="data-card">
<h4>数组数据</h4>
<div id="array-data">列表长度: 0</div>
</div>
</div>
<div class="controls">
<button class="btn-primary" onclick="incrementCount()">增加计数</button>
<button class="btn-secondary" onclick="updateUser()">更新用户</button>
<button class="btn-warning" onclick="addTodo()">添加待办</button>
<button class="btn-danger" onclick="clearLogs()">清空日志</button>
<button class="btn-secondary" onclick="addNewProperty()">添加新属性</button>
<button class="btn-warning" onclick="deleteProperty()">删除属性</button>
</div>
<div class="log-container" id="log-container">
<div class="log-item">🚀 响应式系统已启动...</div>
</div>
</div>
<script>
// 完整的 Vue3 响应式系统实现
class Vue3ReactiveSystem {
constructor() {
this.targetMap = new WeakMap()
this.activeEffect = null
this.effects = new Set()
this.logs = []
}
log(message, type = 'info') {
const timestamp = new Date().toLocaleTimeString()
const logItem = {
time: timestamp,
message,
type
}
this.logs.push(logItem)
const logElement = document.createElement('div')
logElement.className = 'log-item'
logElement.style.borderLeftColor =
type === 'error' ? '#e74c3c' :
type === 'warn' ? '#f39c12' : '#42b883'
logElement.innerHTML = `[${timestamp}] ${message}`
const container = document.getElementById('log-container')
container.appendChild(logElement)
container.scrollTop = container.scrollHeight
console.log(`[${type.toUpperCase()}] ${message}`)
}
track(target, key) {
if (!this.activeEffect) return
let depsMap = this.targetMap.get(target)
if (!depsMap) {
depsMap = new Map()
this.targetMap.set(target, depsMap)
}
let dep = depsMap.get(key)
if (!dep) {
dep = new Set()
depsMap.set(key, dep)
}
if (!dep.has(this.activeEffect)) {
dep.add(this.activeEffect)
this.log(`📌 追踪依赖: ${this.getTargetName(target)}.${key} -> ${this.activeEffect.name}`)
}
}
trigger(target, key, type = 'SET') {
const depsMap = this.targetMap.get(target)
if (!depsMap) return
const dep = depsMap.get(key)
if (dep) {
this.log(`🚀 触发更新: ${this.getTargetName(target)}.${key} (${type}),影响 ${dep.size} 个副作用`)
dep.forEach(effect => {
if (effect.scheduler) {
effect.scheduler()
} else {
effect()
}
})
}
}
getTargetName(target) {
if (target === state) return 'state'
if (target === state.user) return 'state.user'
if (target === state.todos) return 'state.todos'
return 'unknown'
}
reactive(obj, name = 'anonymous') {
const handler = {
get: (target, key, receiver) => {
const result = Reflect.get(target, key, receiver)
// 追踪依赖
this.track(target, key)
// 递归代理对象和数组
if (result !== null && typeof result === 'object') {
return this.reactive(result, `${name}.${String(key)}`)
}
return result
},
set: (target, key, value, receiver) => {
const oldValue = target[key]
const result = Reflect.set(target, key, value, receiver)
if (oldValue !== value) {
this.trigger(target, key, 'SET')
}
return result
},
deleteProperty: (target, key) => {
const hadKey = Object.prototype.hasOwnProperty.call(target, key)
const result = Reflect.deleteProperty(target, key)
if (hadKey && result) {
this.trigger(target, key, 'DELETE')
}
return result
},
has: (target, key) => {
this.track(target, key)
return Reflect.has(target, key)
},
ownKeys: (target) => {
this.track(target, 'ITERATE')
return Reflect.ownKeys(target)
}
}
return new Proxy(obj, handler)
}
effect(fn, options = {}) {
const effectFn = () => {
try {
this.activeEffect = effectFn
return fn()
} finally {
this.activeEffect = null
}
}
effectFn.name = options.name || fn.name || 'anonymous'
effectFn.scheduler = options.scheduler
this.effects.add(effectFn)
// 立即执行一次
effectFn()
return () => {
this.effects.delete(effectFn)
}
}
computed(getter) {
let dirty = true
let value
const effectFn = this.effect(getter, {
name: 'computed',
scheduler: () => {
if (!dirty) {
dirty = true
this.trigger(runner, 'value')
}
}
})
const runner = {
get value() {
if (dirty) {
value = effectFn()
dirty = false
system.track(runner, 'value')
}
return value
}
}
return runner
}
}
// 创建响应式系统实例
const system = new Vue3ReactiveSystem()
// 创建响应式状态
const state = system.reactive({
count: 0,
message: 'Hello Vue3',
user: {
name: '张三',
age: 25,
profile: {
level: 'VIP',
points: 1000
}
},
todos: [],
newProperty: '动态添加的属性'
}, 'state')
// 创建计算属性
const doubleCount = system.computed(() => {
return state.count * 2
})
// 注册副作用函数
system.effect(() => {
document.getElementById('basic-data').textContent =
`计数: ${state.count}, 消息: ${state.message}`
}, { name: 'renderBasicData' })
system.effect(() => {
document.getElementById('user-data').textContent =
`姓名: ${state.user.name}, 年龄: ${state.user.age}, 等级: ${state.user.profile.level}`
}, { name: 'renderUserData' })
system.effect(() => {
document.getElementById('computed-data').textContent =
`双倍计数: ${doubleCount.value}`
}, { name: 'renderComputedData' })
system.effect(() => {
document.getElementById('array-data').textContent =
`列表长度: ${state.todos.length}, 第一个: ${state.todos[0]?.text || '无'}`
}, { name: 'renderArrayData' })
// 测试函数
window.incrementCount = () => {
state.count++
system.log(`⬆️ 计数增加到: ${state.count}`)
}
window.updateUser = () => {
state.user.name = state.user.name === '张三' ? '李四' : '张三'
state.user.age++
state.user.profile.points += 100
system.log(`👤 更新用户信息`)
}
window.addTodo = () => {
const newTodo = {
id: Date.now(),
text: `待办事项 ${state.todos.length + 1}`,
completed: false
}
state.todos.push(newTodo)
system.log(`✅ 添加待办: ${newTodo.text}`)
}
window.addNewProperty = () => {
const newProp = `dynamic_${Date.now()}`
state[newProp] = '动态添加的值'
system.log(`➕ 添加新属性: ${newProp}`)
}
window.deleteProperty = () => {
if (state.newProperty) {
delete state.newProperty
system.log(`➖ 删除属性: newProperty`)
}
}
window.clearLogs = () => {
document.getElementById('log-container').innerHTML = ''
system.logs = []
system.log('🧹 日志已清空')
}
system.log('🎉 Vue3 响应式系统演示已初始化完成')
</script>
</body>
</html>
四、 响应式系统核心原理深度解析
4.1 依赖收集的详细过程
流程图:依赖收集详细流程
flowchart TD
A[读取响应式属性] --> B[Proxy.get 拦截]
B --> C[检查 activeEffect]
C --> D{activeEffect 存在?}
D -- 是 --> E[查找/创建 depsMap]
D -- 否 --> F[直接返回值]
E --> G[查找/创建 dep Set]
G --> H[添加 effect 到 dep]
H --> I[记录依赖关系]
I --> J[返回属性值]
F --> J
4.2 派发更新的详细过程
流程图:派发更新详细流程
flowchart TD
A[修改响应式属性] --> B[Proxy.set 拦截]
B --> C[值是否改变?]
C -- 是 --> D[查找 depsMap]
C -- 否 --> E[直接返回]
D --> F{找到对应的 dep?}
F -- 是 --> G[遍历 dep 中的 effects]
F -- 否 --> E
G --> H[执行调度器或直接执行]
H --> I[effect 重新执行]
I --> J[重新收集依赖]
4.3 嵌套依赖和清理机制
// 高级响应式特性实现
class AdvancedReactiveSystem extends ReactiveSystem {
constructor() {
super()
this.effectStack = [] // 用于处理嵌套 effect
}
effect(fn, options = {}) {
const effectFn = () => {
// 清理之前的依赖
this.cleanup(effectFn)
// 推入 effect 栈
this.effectStack.push(effectFn)
this.activeEffect = effectFn
try {
return fn()
} finally {
// 弹出 effect 栈
this.effectStack.pop()
this.activeEffect = this.effectStack[this.effectStack.length - 1]
}
}
effectFn.name = options.name || fn.name || 'anonymous'
effectFn.scheduler = options.scheduler
effectFn.deps = [] // 存储此 effect 依赖的所有 dep
// 立即执行
effectFn()
return effectFn
}
cleanup(effectFn) {
// 从所有依赖中移除此 effect
for (const dep of effectFn.deps) {
dep.delete(effectFn)
}
effectFn.deps.length = 0
}
track(target, key) {
if (!this.activeEffect) return
let depsMap = this.targetMap.get(target)
if (!depsMap) {
depsMap = new Map()
this.targetMap.set(target, depsMap)
}
let dep = depsMap.get(key)
if (!dep) {
dep = new Set()
depsMap.set(key, dep)
}
if (!dep.has(this.activeEffect)) {
dep.add(this.activeEffect)
// 同时让 effect 记住这个 dep,用于清理
this.activeEffect.deps.push(dep)
}
}
}
五、 响应式 API 深度解析
5.1 ref 的实现原理
class RefImpl {
constructor(value) {
this._value = value
this.dep = new Set()
this.__v_isRef = true
}
get value() {
// 依赖收集
if (activeEffect) {
trackEffects(this.dep)
}
return this._value
}
set value(newVal) {
if (newVal !== this._value) {
this._value = newVal
// 触发更新
triggerEffects(this.dep)
}
}
}
function ref(value) {
return new RefImpl(value)
}
function trackEffects(dep) {
if (activeEffect) {
dep.add(activeEffect)
activeEffect.deps.push(dep)
}
}
function triggerEffects(dep) {
for (const effect of dep) {
if (effect.scheduler) {
effect.scheduler()
} else {
effect()
}
}
}
5.2 computed 的实现原理
class ComputedRefImpl {
constructor(getter) {
this._getter = getter
this._value = undefined
this._dirty = true
this.dep = new Set()
this.effect = effect(getter, {
lazy: true,
scheduler: () => {
if (!this._dirty) {
this._dirty = true
triggerEffects(this.dep)
}
}
})
}
get value() {
if (activeEffect) {
trackEffects(this.dep)
}
if (this._dirty) {
this._value = this.effect()
this._dirty = false
}
return this._value
}
}
function computed(getter) {
return new ComputedRefImpl(getter)
}
5.3 watch 的实现原理
function watch(source, cb, options = {}) {
let getter
if (typeof source === 'function') {
getter = source
} else {
getter = () => traverse(source)
}
let oldValue, newValue
const job = () => {
newValue = effectFn()
cb(newValue, oldValue)
oldValue = newValue
}
const effectFn = effect(
() => getter(),
{
lazy: true,
scheduler: job
}
)
if (options.immediate) {
job()
} else {
oldValue = effectFn()
}
}
function traverse(value, seen = new Set()) {
if (typeof value !== 'object' || value === null || seen.has(value)) {
return value
}
seen.add(value)
for (const key in value) {
traverse(value[key], seen)
}
return value
}
六、 响应式系统性能优化
6.1 响应式系统性能对比
// 性能测试示例
function performanceTest() {
const system = new ReactiveSystem()
// 创建大型响应式对象
const largeData = system.reactive(
Array.from({ length: 1000 }, (_, i) => ({
id: i,
name: `Item ${i}`,
value: Math.random(),
nested: {
level1: {
level2: {
data: `Nested ${i}`
}
}
}
}))
)
// 测试读取性能
console.time('读取性能')
system.effect(() => {
let total = 0
for (let i = 0; i < largeData.length; i++) {
total += largeData[i].value
}
return total
}, { name: 'performanceTest' })
console.timeEnd('读取性能')
// 测试更新性能
console.time('更新性能')
for (let i = 0; i < 100; i++) {
largeData[i].value = Math.random()
}
console.timeEnd('更新性能')
}
// 运行性能测试
performanceTest()
6.2 优化技巧
// 1. 使用 shallowRef 避免深度响应式
function shallowRef(value) {
return {
_value: value,
get value() {
trackEffects(this.dep)
return this._value
},
set value(newVal) {
this._value = newVal
triggerEffects(this.dep)
}
}
}
// 2. 使用 markRaw 标记非响应式对象
function markRaw(obj) {
obj.__v_skip = true
return obj
}
// 3. 合理使用 computed 缓存计算结果
const expensiveValue = computed(() => {
// 复杂计算...
return heavyCalculation()
})
七、 实际应用场景
7.1 组件状态管理
// 使用响应式系统实现简单的状态管理
class Store {
constructor() {
this.system = new ReactiveSystem()
this.state = this.system.reactive({
user: null,
settings: {
theme: 'light',
language: 'zh-CN'
},
cart: []
})
this.getters = {
cartTotal: this.system.computed(() =>
this.state.cart.reduce((total, item) => total + item.price, 0)
),
itemCount: this.system.computed(() => this.state.cart.length)
}
this.mutations = {
setUser: (user) => { this.state.user = user },
addToCart: (item) => { this.state.cart.push(item) },
updateSettings: (settings) => {
Object.assign(this.state.settings, settings)
}
}
}
}
7.2 表单处理
// 响应式表单处理
function useForm(initialValues = {}) {
const system = new ReactiveSystem()
const form = system.reactive({ ...initialValues })
const errors = system.reactive({})
const touched = system.reactive({})
const validate = system.computed(() => {
const result = {}
// 验证逻辑...
return result
})
const isValid = system.computed(() =>
Object.keys(validate.value).length === 0
)
return {
form,
errors,
touched,
validate,
isValid
}
}
八、 总结
8.1 Vue3 响应式系统的核心优势
- 更精确的依赖追踪:基于 Proxy 的全面拦截
- 更好的性能:惰性代理和按需响应
- 更完善的功能:支持 Map、Set、数组方法等
- 更好的开发体验:完整的 TypeScript 支持
8.2 响应式系统核心机制
- 依赖收集:在 get 操作时建立属性与 effect 的映射
- 派发更新:在 set 操作时通知相关 effect 重新执行
- 清理机制:每次 effect 执行前清理旧依赖
- 调度系统:支持异步更新和批量更新
8.3 性能优化要点
- 避免不必要的深度响应式:使用 shallowRef
- 合理使用计算属性:缓存复杂计算结果
- 注意内存泄漏:及时清理不需要的 effect
- 批量更新:使用 nextTick 合并多次更新
Vue3 的响应式系统通过精妙的设计,实现了高效、精确的数据变化追踪,为现代前端应用提供了强大的数据驱动能力。理解其工作原理对于编写高性能的 Vue 应用至关重要。
如果这篇文章对你有帮助,欢迎点赞、收藏和评论!有任何问题都可以在评论区讨论。