Vue3 的响应式系统源码级数组的响应式处理与索引修改优化原理
字数 1068 2025-11-27 04:48:08
Vue3 的响应式系统源码级数组的响应式处理与索引修改优化原理
一、问题描述
Vue3 的响应式系统需要处理数组的特殊情况,比如直接通过索引修改元素(arr[0] = newValue)或调用数组方法(push、pop 等)。由于 JavaScript 的限制,直接通过索引修改数组元素不会触发 Proxy 的 set 陷阱,且某些数组方法会同时读取和修改数组,可能导致不必要的依赖收集和重复触发更新。Vue3 通过重写数组方法和优化索引修改逻辑来解决这些问题。
二、数组的响应式处理原理
-
数组的 Proxy 代理
- 数组也是对象,因此 Vue3 会使用
reactive函数为数组创建 Proxy 代理。 - 但与普通对象不同,数组的某些方法(如
push、pop、splice等)会修改数组长度或内容,需要特殊处理。
- 数组也是对象,因此 Vue3 会使用
-
重写数组方法
- Vue3 创建了一个包含 7 个变异方法(
push、pop、shift、unshift、splice、sort、reverse)的数组arrayInstrumentations。 - 这些方法被重写,以便在调用时手动触发依赖更新。例如:
const arrayInstrumentations = { push(...args) { // 暂停依赖收集(避免因读取数组长度而收集多余依赖) pauseTracking() const res = Array.prototype.push.call(this, ...args) resetTracking() // 手动触发更新 trigger(this, 'add', { index: this.length - 1, value: args[0] }) return res } } - 在 Proxy 的 get 陷阱中,如果访问的是这些方法,则返回重写后的版本:
function createGetter() { return (target, key, receiver) => { if (key === 'length' || (typeof key === 'symbol' && key !== Symbol.iterator)) { track(target, key) // 收集依赖 } // 如果是重写的数组方法,返回 arrayInstrumentations 中的版本 if (Array.isArray(target) && arrayInstrumentations.hasOwnProperty(key)) { return Reflect.get(arrayInstrumentations, key, receiver) } // 其他情况正常返回 return Reflect.get(target, key, receiver) } }
- Vue3 创建了一个包含 7 个变异方法(
三、索引修改的优化原理
-
直接索引修改的问题
- 当执行
arr[i] = newValue时,Proxy 的 set 陷阱会被触发,但 Vue3 需要判断此次修改是新增元素(i >= arr.length)还是修改现有元素。 - 如果是新增元素,需要触发
add类型的更新;如果是修改,则触发set类型。
- 当执行
-
优化逻辑
- 在 set 陷阱中,通过比较
key(索引)和target.length来判断操作类型:function createSetter() { return (target, key, value, receiver) => { const oldValue = target[key] // 判断是新增还是修改 const type = Array.isArray(target) ? (Number(key) < target.length ? 'set' : 'add') : (hasOwn(target, key) ? 'set' : 'add') const res = Reflect.set(target, key, value, receiver) // 触发更新,传递类型信息 trigger(target, key, type, value) return res } } - 这样,Vue3 可以精确地触发更新,避免不必要的重渲染。
- 在 set 陷阱中,通过比较
-
避免重复触发
- 某些数组方法(如
splice)会先读取数组属性(如length),再修改数组,导致依赖被重复收集。 - 重写方法时,Vue3 使用
pauseTracking()和resetTracking()临时暂停依赖收集,确保只在方法执行完毕后触发一次更新。
- 某些数组方法(如
四、总结
Vue3 通过重写数组方法和优化索引修改逻辑,解决了数组响应式处理的特殊问题。重写的方法确保了变异操作能正确触发更新,而索引修改的优化则精确区分新增和修改操作,避免不必要的更新。这些优化使得 Vue3 的数组响应式处理既高效又准确。