关于vue.js:理解Vue中响应式数据

数组和对象类型当值变动时如何劫持到?

a、对象通过Object.defineProperty将属性进行劫持;多重对象通过递归进行实现。

export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  // 1.如果对象不可配置则间接退出
  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    return
  }

  // 2.获取getter和setter
  const getter = property && property.get
  const setter = property && property.set
  
  // 3.从新定义set和get办法
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      return value
    },
    set: function reactiveSetter (newVal) {
      const value = getter ? getter.call(obj) : val
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      if (getter && !setter) return
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
    }
  })
}

这种形式只会监听对象自带的属性,新增的属性监听不到,因而对于新增的属性应用$set形式进行数据新增,$set办法外部会将新增属性定义成响应式数据
b、数组通过重写办法

export const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]
methodsToPatch.forEach(function (method) {
  // cache original 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)
    return result
  })
})

当操作数组时会调用重写的数组办法,这时就能够监测到数据的变动。因而,如果批改数组索引和长度是不会监听到数组的变动的。

Vue3中应用ES6的Proxy代理,Proxy能够拦挡指标对象的底层操作

let obj = {
    name: {name: 'lh'}
}
let handler = {
    get(target,key){ // 这里的命名必须和Reflect办法对应
        if(typeof target[key] === 'object' && target[key] !== null){
            return new Proxy(target[key],handler);
        }
        return Reflect.get(target,key);
    },
    set(target,key,value){ 
        let oldValue = target[key];
        if(!oldValue){
            console.log('新增属性')
        }else if(oldValue !== value){
            console.log('批改属性')
        }
        return Reflect.set(target,key,value);
    }
}
let proxy = new Proxy(obj,handler);

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理