响应式系统原理

  • Published2026-07-30 11:12:34

响应式系统原理响应式系统概述Vue3.0 的响应式系统是其核心特性之一,它能够自动追踪数据的变化并触发相应的更新。与 Vue2 相比,Vue3.0 使用 Proxy 替代了 Object.defineProperty,带来了更好的性能和更强大的功能。

核心概念:

响应式数据:能够自动追踪变化的数据副作用函数:依赖响应式数据的函数,当数据变化时需要重新执行依赖收集:自动追踪哪些数据被哪些函数使用触发更新:当数据变化时,自动执行相关的副作用函数Proxy vs Object.defineProperty1. 性能对比Object.defineProperty 的局限性:

只能监听对象的属性,无法监听数组索引和长度变化需要递归遍历对象的所有属性新增属性无法自动响应性能开销较大,特别是在大型对象上Proxy 的优势:

可以监听对象的所有操作(get、set、delete、has 等)支持数组索引和长度变化监听新增属性自动响应性能更好,不需要递归遍历2. 实际使用场景对比Vue2 中的问题:

// 数组变化监听问题

const items = ['a', 'b', 'c']

// 以下操作在 Vue2 中不会触发更新

items[0] = 'x' // 索引赋值

items.length = 0 // 长度变化

items[3] = 'd' // 新增索引

// 对象新增属性问题

const user = { name: 'John' }

// 新增属性不会响应

user.age = 25Vue3.0 的改进:

// 所有操作都能正确响应

const items = reactive(['a', 'b', 'c'])

items[0] = 'x' // 自动触发更新

items.length = 0 // 自动触发更新

items[3] = 'd' // 自动触发更新

const user = reactive({ name: 'John' })

user.age = 25 // 自动触发更新响应式系统核心实现1. 响应式对象创建reactive 函数实现原理:

// 简化的 reactive 实现

function reactive(target) {

// 检查是否已经是响应式对象

if (isReactive(target)) {

return target

}

// 创建响应式代理

const proxy = new Proxy(target, {

get(target, key, receiver) {

// 依赖收集

track(target, key)

// 获取值

const result = Reflect.get(target, key, receiver)

// 如果值是对象,递归创建响应式

if (isObject(result)) {

return reactive(result)

}

return result

},

set(target, key, value, receiver) {

// 设置值

const oldValue = target[key]

const result = Reflect.set(target, key, value, receiver)

// 如果值发生变化,触发更新

if (oldValue !== value) {

trigger(target, key)

}

return result

},

deleteProperty(target, key) {

// 删除属性

const hadKey = hasOwn(target, key)

const result = Reflect.deleteProperty(target, key)

// 如果属性存在,触发更新

if (result && hadKey) {

trigger(target, key)

}

return result

}

})

return proxy

}2. ref 实现原理ref 的特殊处理:

// 简化的 ref 实现

function ref(value) {

// 如果值已经是 ref,直接返回

if (isRef(value)) {

return value

}

// 创建 ref 对象

const refObject = {

__v_isRef: true,

get value() {

// 依赖收集

track(refObject, 'value')

return value

},

set value(newValue) {

// 如果值发生变化,触发更新

if (value !== newValue) {

value = newValue

trigger(refObject, 'value')

}

}

}

return refObject

}依赖收集与触发机制1. 依赖收集(track)track 函数的作用:

记录当前正在执行的副作用函数建立数据与副作用函数的关联关系为后续的更新触发做准备实现原理:

// 全局的副作用函数栈

const effectStack = []

let activeEffect = null

function track(target, key) {

// 如果没有活跃的副作用函数,直接返回

if (!activeEffect) return

// 获取或创建依赖集合

let depsMap = targetMap.get(target)

if (!depsMap) {

targetMap.set(target, (depsMap = new Map()))

}

let dep = depsMap.get(key)

if (!dep) {

depsMap.set(key, (dep = new Set()))

}

// 添加依赖关系

dep.add(activeEffect)

activeEffect.deps.push(dep)

}2. 触发更新(trigger)trigger 函数的作用:

当数据变化时,找到所有相关的副作用函数将这些函数加入更新队列在适当的时机执行更新实现原理:

function trigger(target, key) {

// 获取依赖集合

const depsMap = targetMap.get(target)

if (!depsMap) return

const dep = depsMap.get(key)

if (!dep) return

// 创建要执行的副作用函数集合

const effects = new Set()

dep.forEach(effect => {

// 避免无限递归

if (effect !== activeEffect) {

effects.add(effect)

}

})

// 执行副作用函数

effects.forEach(effect => {

if (effect.scheduler) {

effect.scheduler()

} else {

effect.run()

}

})

}副作用函数管理1. effect 函数effect 的作用:

创建副作用函数自动执行依赖收集管理副作用函数的生命周期基本实现:

function effect(fn, options = {}) {

const effect = new ReactiveEffect(fn, options.scheduler)

if (!options.lazy) {

effect.run()

}

return effect.run.bind(effect)

}

class ReactiveEffect {

constructor(fn, scheduler) {

this.fn = fn

this.scheduler = scheduler

this.deps = []

}

run() {

try {

// 将当前 effect 设为活跃状态

activeEffect = this

effectStack.push(this)

// 执行函数,触发依赖收集

return this.fn()

} finally {

// 恢复之前的状态

effectStack.pop()

activeEffect = effectStack[effectStack.length - 1]

}

}

stop() {

// 清理依赖关系

this.deps.forEach(dep => dep.delete(this))

this.deps.length = 0

}

}2. 实际应用场景计算属性的实现:

function computed(getter) {

let value

let dirty = true

const effect = new ReactiveEffect(getter, () => {

// 当依赖变化时,标记为脏数据

dirty = true

})

const computedRef = {

get value() {

if (dirty) {

value = effect.run()

dirty = false

}

return value

}

}

return computedRef

}监听器的实现:

function watch(source, callback, options = {}) {

let cleanup

const effect = new ReactiveEffect(() => {

// 获取监听的值

const value = typeof source === 'function' ? source() : source

// 执行清理函数

if (cleanup) {

cleanup()

}

// 执行回调

callback(value, undefined, (onCleanup) => {

cleanup = onCleanup

})

})

effect.run()

return () => effect.stop()

}响应式系统的优化1. 浅响应式(shallowReactive)使用场景:

只需要对象第一层属性响应式减少深层对象的响应式开销提高性能实现原理:

function shallowReactive(target) {

return new Proxy(target, {

get(target, key, receiver) {

track(target, key)

const result = Reflect.get(target, key, receiver)

// 不递归创建响应式

return result

},

set(target, key, value, receiver) {

const oldValue = target[key]

const result = Reflect.set(target, key, value, receiver)

if (oldValue !== value) {

trigger(target, key)

}

return result

}

})

}2. 只读响应式(readonly)使用场景:

防止数据被意外修改提高数据安全性减少不必要的更新实现原理:

function readonly(target) {

return new Proxy(target, {

get(target, key, receiver) {

track(target, key)

const result = Reflect.get(target, key, receiver)

if (isObject(result)) {

return readonly(result)

}

return result

},

set() {

// 只读对象不允许修改

console.warn('Cannot modify readonly object')

return false

},

deleteProperty() {

// 只读对象不允许删除属性

console.warn('Cannot delete property from readonly object')

return false

}

})

}实际应用中的注意事项1. 响应式丢失问题常见问题:

// 解构赋值会丢失响应式

const user = reactive({ name: 'John', age: 25 })

const { name, age } = user // 解构后不再是响应式

// 正确做法

const name = computed(() => user.name)

const age = computed(() => user.age)

// 或者使用 toRefs

const { name, age } = toRefs(user)2. 循环引用处理避免无限递归:

function reactive(target) {

// 使用 WeakMap 记录已创建的响应式对象

const reactiveMap = new WeakMap()

if (reactiveMap.has(target)) {

return reactiveMap.get(target)

}

const proxy = new Proxy(target, {

get(target, key, receiver) {

const result = Reflect.get(target, key, receiver)

if (isObject(result)) {

// 检查循环引用

if (result === target) {

return result

}

return reactive(result)

}

return result

}

})

reactiveMap.set(target, proxy)

return proxy

}3. 性能优化建议合理使用响应式:

避免在大型对象上使用 reactive使用 shallowReactive 处理深层嵌套对象及时清理不需要的响应式对象合理使用 computed 缓存计算结果总结Vue3.0 的响应式系统通过 Proxy 实现了更强大和高效的响应式能力。理解其工作原理有助于我们更好地使用这些特性,避免常见问题,并编写出性能更好的代码。在实际开发中,合理使用响应式系统可以大大提升开发效率和用户体验。

下一步学习现在您已经深入了解了 Vue3.0 的响应式系统原理,建议按以下顺序继续学习:

🧩 组件化开发组件化开发 - 学习如何在实际项目中使用响应式系统构建组件,掌握组件间的通信模式和最佳实践。

📝 TypeScript 集成TypeScript 集成 - 学习如何为响应式数据添加类型定义,提升代码的类型安全性和开发体验。

⚡ 性能优化性能优化 - 学习如何利用响应式系统的特性进行性能优化,掌握编译时和运行时的优化策略。

学习建议深入理解:尝试自己实现简单的响应式系统,加深对原理的理解性能监控:使用 Vue DevTools 观察响应式数据的变化和性能影响最佳实践:避免常见的响应式陷阱,如解构赋值丢失响应式面试准备:重点掌握 Proxy vs Object.defineProperty 的区别和优势常见问题Q: 为什么 Vue3.0 选择使用 Proxy 而不是 Object.defineProperty?A: Proxy 可以监听更多操作(如数组索引变化、属性删除等),性能更好,不需要递归遍历对象。

Q: 如何避免响应式丢失问题?A: 使用 toRefs 解构对象,或者使用 computed 包装需要响应的值。

Q: 什么时候使用 shallowReactive?A: 当只需要对象第一层属性响应式时,使用 shallowReactive 可以减少性能开销。

Q: 响应式系统如何处理循环引用?A: Vue3.0 使用 WeakMap 记录已创建的响应式对象,避免无限递归。

准备好学习组件化开发了吗? 点击 组件化开发 继续构建可复用的 Vue3.0 组件! 🧩