vue数组响应式原理

vue2中Object.defineProperty响应式只对对象有效,对数组无效,所以对数组做额外处理。我们知道,会改变数组本身的方法只有7个:sort, push, pop, slice, splice, shift, unshift,所以可以通过重写这些方法来达到数组响应式
解决方案:
1. 找到数组原型
2. 覆盖那些能够修改数组的更新方法,让他们在修改数组同时,还可以通知更新
3. 将得到的新的原型设置到数组实例原型上
4. 对数组内部元素实现响应式

// 实现数组响应式

// 1. 替换数组原型中7个方法
const originalProto = Array.prototype
// 克隆体原数组原型
const arrayProto = Object.create(originalProto)
// 可修改数组的7个方法 , 'sort'
const changeMethods = ['push', 'pop', 'shift', 'unshift', 'slice', 'splice', 'sort']
//  2. 在克隆的原型上,覆盖那些能够修改数组的更新方法,让他们在修改数组同时,还可以通知更新
changeMethods.forEach(method => {
  arrayProto[method] = function() {
    // 进行原始操作
    originalProto[method].apply(this, arguments)
    // 覆盖操作:增加更新通知
    console.log(`数组正在执行${method}方法`);
  }
})



// 对象响应化
function defineReactive(obj, key, value) {
  Object.defineProperty(obj, key, {
    get() {
      console.log('获取' + key);
      return value
    },
    set(newVal) {
      if(newVal !== value) {
        // console.log(newVal);
        // console.log(JSON.stringify(obj[key]));
        console.log(`正在改变${key}值:从${obj[key]}变为${newVal}`)
        value = newVal
      }
    }
  })
}

function observer(obj) {
  // 不是对象或者为null,不做响应式,结束
  if(typeof obj !== 'object' || obj === null) return;
  // 如果是数组,修改其实例的原型
  if(Array.isArray(obj)) {
    // 3. 将得到的新的原型设置到数组实例原型上
    obj.__proto__ = arrayProto
    
    // 4. 对数组内的元素,同样进行响应化
    for(let i = 0; i < obj.length; i++) {
      // console.log(obj[i]);
      observer(obj[i])
    }

  // 如果是对象
  }else {
    Object.keys(obj).forEach(key => {
      console.log(obj, key, obj[key]);
      defineReactive(obj, key, obj[key])
    })
  }
}

obj = [{a: 1}, 2, 7, 5 ,3]
observer(obj)
obj.push(4) // 数组正在执行push方法
obj.pop() // 数组正在执行pop方法
obj[0].a = 2  
// 获取a    
// 正在改变a值:从1变为2
obj.sort()  // 数组正在执行sort方法
console.log(obj); // [ 2, 3, 5, 7, { a: [Getter/Setter] } ]

console.log(obj[4].a); 
// 获取a  
// 2

你可能感兴趣的:(vue数组响应式原理)