深入解析Vue响应式系统的设计与实现

前言:为什么Vue的响应式值得深究?

在当今前端框架三足鼎立的格局中,Vue以其优雅的API设计和渐进式特性独树一帜。而其中最令人称道的,莫过于其精妙的响应式系统。本文将带您深入Vue的响应式核心,解析其设计哲学与实现细节,揭示这个看似简单实则暗藏玄机的系统如何支撑起整个Vue框架。


一、响应式系统的三层架构设计

1.1 观察者模式的三重奏

Vue的响应式系统建立在经典的观察者模式之上,但进行了创新性的三层架构设计:
A[数据劫持层] –>|通知| B[依赖收集层]
B –>|管理| C[视图更新层]

1.1.1 数据劫持层(Object.defineProperty/Proxy)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function defineReactive(obj, key) {
const dep = new Dep()
let val = obj[key]

Object.defineProperty(obj, key, {
get() {
if (Dep.target) {
dep.depend() // 依赖收集
}
return val
},
set(newVal) {
if (newVal === val) return
val = newVal
dep.notify() // 派发更新
}
})
}

1.1.2 依赖收集层(Dep类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Dep {
constructor() {
this.subs = new Set()
}

depend() {
if (Dep.target) {
this.subs.add(Dep.target)
}
}

notify() {
this.subs.forEach(watcher => watcher.update())
}
}

1.1.3 视图更新层(Watcher类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Watcher {
constructor(vm, expOrFn) {
this.vm = vm
this.getter = parsePath(expOrFn)
this.value = this.get()
}

get() {
Dep.target = this
const value = this.getter.call(this.vm, this.vm)
Dep.target = null
return value
}

update() {
queueWatcher(this) // 加入异步更新队列
}
}

二、响应式系统的四大精妙设计

2.1 依赖收集的精准追踪

通过Dep.target的巧妙设计,Vue实现了:自动化的依赖收集精确到属性的订阅关系避免不必要的重复更新

2.2 异步更新队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 更新队列实现核心
const queue = []
let waiting = false
function queueWatcher(watcher) {
if (!queue.includes(watcher)) {
queue.push(watcher)
}
if (!waiting) {
waiting = true
nextTick(flushQueue)
}
}

function flushQueue() {
queue.forEach(watcher => watcher.run())
queue.length = 0
waiting = false
}

异步更新的优势:合并同一事件循环内的多次更新保证更新顺序的一致性避免不必要的重复计算

2.3 嵌套对象的深度观测

通过递归劫持实现了深层响应:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function observe(value) {
if (typeof value !== 'object') return

let ob
if (hasOwn(value, '__ob__')) {
ob = value.__ob__
} else {
ob = new Observer(value)
}
return ob
}

class Observer {
constructor(value) {
this.value = value
if (Array.isArray(value)) {
// 数组的特殊处理
} else {
this.walk(value)
}
}

walk(obj) {
Object.keys(obj).forEach(key => {
defineReactive(obj, key)
})
}
}

2.4 数组方法的hack处理

通过重写数组原型方法实现响应式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto)
;['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(method => {
const original = arrayProto[method]
def(arrayMethods, method, function mutator(...args) {
const result = original.apply(this, args)
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
ob.dep.notify()
return result
})
})

三、Vue3响应式系统的革命性升级

3.1 Proxy带来的性能飞跃

1
2
3
4
5
6
7
8
9
10
11
12
13
function reactive(target) {
return new Proxy(target, {
get(target, key, receiver) {
track(target, key)
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver)
trigger(target, key)
return result
}
})
}

性能优化点:懒代理机制精确的类型判断更高效的内存使用

3.2 依赖收集的进化

1
2
3
4
5
6
7
8
9
10
11
12
13
const targetMap = new WeakMap()
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)
}