主题
源码分析-响应式原理
1. 响应式系统基础
1.1 核心概念
Vue的响应式系统主要由以下几个部分组成:
- Observer:数据劫持,负责将数据对象的属性转换为getter/setter
- Dep:依赖收集器,用于存储和通知Watcher
- Watcher:观察者,负责监听数据变化并触发回调
1.2 基本原理
js
// Vue2.x响应式实现核心代码
function defineReactive(obj, key, val) {
// 为每个属性创建一个Dep实例
const dep = new Dep()
Object.defineProperty(obj, key, {
get() {
// 依赖收集
if (Dep.target) {
dep.depend()
}
return val
},
set(newVal) {
if (val === newVal) return
val = newVal
// 通知更新
dep.notify()
}
})
}
2. Observer实现
2.1 对象的响应式处理
js
class Observer {
constructor(value) {
this.value = value
this.dep = new Dep()
def(value, '__ob__', this)
if (Array.isArray(value)) {
// 数组响应式处理
this.observeArray(value)
} else {
// 对象响应式处理
this.walk(value)
}
}
walk(obj) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i], obj[keys[i]])
}
}
observeArray(items) {
for (let i = 0; i < items.length; i++) {
observe(items[i])
}
}
}
2.2 数组方法的拦截
js
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
})
})
3. 依赖收集
3.1 Dep实现
js
class Dep {
constructor() {
this.subs = []
}
addSub(sub) {
this.subs.push(sub)
}
removeSub(sub) {
remove(this.subs, sub)
}
depend() {
if (Dep.target) {
Dep.target.addDep(this)
}
}
notify() {
const subs = this.subs.slice()
for (let i = 0; i < subs.length; i++) {
subs[i].update()
}
}
}
// 当前正在评估的Watcher
Dep.target = null
const targetStack = []
3.2 依赖收集过程
js
function pushTarget(watcher) {
if (Dep.target) targetStack.push(Dep.target)
Dep.target = watcher
}
function popTarget() {
Dep.target = targetStack.pop()
}
4. Watcher实现
4.1 基本结构
js
class Watcher {
constructor(vm, expOrFn, cb, options) {
this.vm = vm
this.cb = cb
this.deps = []
this.newDeps = []
this.depIds = new Set()
this.newDepIds = new Set()
if (typeof expOrFn === 'function') {
this.getter = expOrFn
} else {
this.getter = parsePath(expOrFn)
}
this.value = this.get()
}
get() {
pushTarget(this)
let value
try {
value = this.getter.call(this.vm, this.vm)
} finally {
popTarget()
this.cleanupDeps()
}
return value
}
}
4.2 更新机制
js
update() {
if (this.lazy) {
this.dirty = true
} else if (this.sync) {
this.run()
} else {
queueWatcher(this)
}
}
run() {
const value = this.get()
const oldValue = this.value
this.value = value
if (this.user) {
try {
this.cb.call(this.vm, value, oldValue)
} catch (e) {
handleError(e, this.vm, `callback for watcher "${this.expression}"`)
}
} else {
this.cb.call(this.vm, value, oldValue)
}
}
5. Vue3的响应式系统
5.1 Proxy实现
js
function reactive(target) {
const handler = {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver)
track(target, key)
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
}
}
return new Proxy(target, handler)
}
5.2 依赖追踪
js
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)
}
6. 性能优化
6.1 批量更新
js
const queue = []
let flushing = false
let waiting = false
function queueWatcher(watcher) {
if (!queue.includes(watcher)) {
queue.push(watcher)
if (!waiting) {
waiting = true
nextTick(flushSchedulerQueue)
}
}
}
6.2 深度监听优化
js
function observe(value, asRootData) {
if (!isObject(value)) return
let ob
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__
} else if (
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value)
) {
ob = new Observer(value)
}
return ob
}
7. 常见问题处理
7.1 数组变更检测
js
// 重写数组方法
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
methodsToPatch.forEach(method => {
const original = arrayProto[method]
def(arrayMethods, method, function (...args) {
const result = original.apply(this, args)
const ob = this.__ob__
ob.dep.notify()
return result
})
})
7.2 对象新属性处理
js
function set(target, key, val) {
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
return val
}
if (key in target && !(key in Object.prototype)) {
target[key] = val
return val
}
const ob = target.__ob__
if (!ob) {
target[key] = val
return val
}
defineReactive(ob.value, key, val)
ob.dep.notify()
return val
}
8. 注意事项
- 避免大量深层次的响应式数据
- 合理使用Object.freeze()
- 注意数组变异方法的限制
- 理解Vue3的响应式差异
- 性能优化的时机选择