Responsive data changes in vue2

1. How Vue2 implements data responsiveness

VueThe way to achieve responsive changes is through data hijacking and publish-subscribe patterns.

  1. Data hijacking: Hijacking the properties of a data object Vueby using methods so that it can trigger corresponding operations when the property value changes.Object.defineProperty()

  2. Publish-Subscribe Pattern: VueUse the Publish-Subscribe pattern to listen for data changes and notify relevant subscribers to update the view when the changes occur. When the data changes, the corresponding setter method will be triggered, and then all subscribers will be notified for updates.

The specific implementation steps are as follows:

  1. When initializing Vuethe instance, by traversing the data object, use Object.defineProperty()methods to convert each attribute into gettera sum setter.

  2. In getterthe method, add the Subscriber Watcherobject to the dependency list of the current property.

  3. In setterthe method, when the data changes, the update operation of all subscribers of the property will be triggered, that is, Watcherthe object's updatemethod is added to the asynchronous update queue.

  4. When all synchronization tasks are completed, the asynchronous update queue will execute the methods Watcherof each object in sequence updateand update the view.

Through the combination of data hijacking and publish-subscribe mode, Vuethe view can be updated in time when the data changes, achieving responsive changes.

export function defineReactive ( // 定义响应式数据

  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean

) {
    
    
  const dep = new Dep()
  // 如果不可以配置直接return

  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    
    
    return

 }
  // cater for pre-defined getter/setters

  const getter = property && property.get

  const setter = property && property.set

  if ((!getter || setter) && arguments.length === 2) {
    
    
    val = obj[key]
 }
  // 对数据进行观测

  let childOb = !shallow && observe(val)
  Object.defineProperty(obj, key, {
    
    
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
    
     // 取数据时进行依赖收集

      const value = getter ? getter.call(obj) : val

      if (Dep.target) {
    
    
        dep.depend()
        if (childOb) {
    
     // 让对象本身进行依赖收集

          childOb.dep.depend()  // {a:1} => {} 外层对象

          if (Array.isArray(value)) {
    
     // 如果是数组 {arr:[[],[]]} 
vm.arr取值只会让arr属性和外层数组进行收集   

            dependArray(value) 
         }
       }
     }
      return value

   },
    set: function reactiveSetter (newVal) {
    
    
      const value = getter ? getter.call(obj) : val

      /* eslint-disable no-self-compare */

      if (newVal === value || (newVal !== newVal && value !== value)) {
    
    return}
      /* eslint-enable no-self-compare */

      if (process.env.NODE_ENV !== 'production' && customSetter) {
    
    
        customSetter()
     }
      // #7981: for accessor properties without setter

      if (getter && !setter) return

      if (setter) {
    
    
        setter.call(obj, newVal)
     } else {
    
    
        val = newVal

     }
      childOb = !shallow && observe(newVal)
      dep.notify()
   }
 })
}

2. How to detect changes in arrays in vue2

For performance reasons, the array does not use defineProperty to intercept each item of the array. Instead, it chooses to rewrite
the array (push, shift, pop, splice, unshift, sort, reverse) methods.
If the array is an object data type, it will also be hijacked recursively.

The index and length changes of the array cannot be monitored

Detecting changes in the array is achieved by overriding the relevant methods on the array prototype. Specific steps are as follows:

  1. First, Vue will determine whether the current browser supports native array methods. If supported, directly override the method on the array prototype and add the corresponding mutation method to the overridden method. If it is not supported, create a new object and use Object.defineProperty to intercept the array mutation method.

  2. When overriding array methods, Vue will first cache the native array methods, such as Array.prototype.push, Array.prototype.popetc. Then, in the overridden method, the cached native method is first called, and then different operations are performed according to different mutation method types, such as adding responsive elements, triggering dependency updates, etc.

  3. Vue will also determine whether __proto__the attribute is supported. If it is supported, it will directly point the array's prototype to the overridden prototype object so that the array's prototype chain can normally find the overridden method. If it is not supported, traverse the overridden prototype object and copy the methods to the array instance.

The sample code is as follows:

// 是否支持原生数组方法
const hasProto = '__proto__' in {
    
    };
// 缓存原生数组方法
const arrayProto = Array.prototype;
// 创建重写的原型对象
const arrayMethods = Object.create(arrayProto);

// 定义重写的原型方法
['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(function (method) {
    
    
  // 缓存原生数组方法
  const original = arrayProto[method];
  // 重写原型方法
  def(arrayMethods, method, function mutator(...args) {
    
    
    // 调用原生数组方法
    const result = original.apply(this, args);
    // 获取当前数组的__ob__属性,即Observer实例
    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;
  });
});

// 将重写的原型对象设置到数组的原型上
const arrayKeys = Object.getOwnPropertyNames(arrayMethods);
if (hasProto) {
    
    
  protoAugment(target, arrayMethods);
} else {
    
    
  copyAugment(target, arrayMethods, arrayKeys);
}

Through the above code, Vue realizes the detection of array changes and can automatically track the operations of the array to achieve responsive updates.

Guess you like

Origin blog.csdn.net/jieyucx/article/details/134534625