1.1 Vue2 中采用重写数组方法的方式
- 数组考虑性能原因没有用
defineProperty
对数组的每一项进行拦截,而是选择重写数组(push,shift,pop,splice,unshift,sort,reverse
)方法。数组中如果是对象数据类型也会进行递归劫持。
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
methodsToPatch.forEach(function (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)
if (__DEV__) {
ob.dep.notify({
type: TriggerOpTypes.ARRAY_MUTATION,
target: this,
key: method
})
} else {
ob.dep.notify()
}
return result
})
})
1.2 Vue3 直接采用的是 Proxy
- 在 Vue 3.x 中,直接使用 Proxy 实现了更高效和精确的数组变化检测,通过 Proxy,Vue 可以捕获到数组索引和长度的变化,不再需要重写数组的方法。这是 Vue 3.x 在性能方面的一个重要改进(但是由于代理问题,还需要对部分检测方法进行重写)。
const obj = {n:1}
const arr = reactive(obj)
arr.includes(obj)
function createArrayInstrumentations() {
const instrumentations: Record<string, Function> = {}
;(['includes', 'indexOf', 'lastIndexOf'] as const).forEach(key => {
instrumentations[key] = function (this: unknown[], ...args: unknown[]) {
const arr = toRaw(this) as any
for (let i = 0, l = this.length; i < l; i++) {
track(arr, TrackOpTypes.GET, i + '')
}
const res = arr[key](...args)
if (res === -1 || res === false) {
return arr[key](...args.map(toRaw))
} else {
return res
}
}
})
;(['push', 'pop', 'shift', 'unshift', 'splice'] as const).forEach(key => {
instrumentations[key] = function (this: unknown[], ...args: unknown[]) {
pauseTracking()
pauseScheduling()
const res = (toRaw(this) as any)[key].apply(this, args)
resetScheduling()
resetTracking()
return res
}
})
return instrumentations
}
Vue2 数组重写,Vue3 数组重写