Principio de cambio de monitoreo de 9 atributos de la extensión básica de Vue

// vue属性监测变更原理

  // 概述: 在使用vue中我们在data中定义的属性,能够在改变后,页面能够监听这些变化,下面演示属性监测变更的一些原理。

  // 1、定义一个对象,用get、set改写属性, 即在get和set可以定义扩展的功能,而vue属性监测变更主要是通过同一个Dep对象
  // 实现get,set的变更监听。即get时(如页面使用属性时),包装使用这些者属性为一个(Watch对象),添加到Dep中,而set时
  //,调用这些所有, 添加的监听。

  var obj = {
    id:1,
    name:'name',
    price:3000,
  };

  var value = obj.price;

  class DepDemo {
    depend () {
      // 如果调用get访问器,那么就是使用这个属性的,就添加到依赖
    }
    notify() {
      // 如果set访问器执行,那么就通知所有添加的依赖对象。
    }
  };

  var depDemo = new DepDemo();

  Object.defineProperty(obj, 'price', {
    enumerable: true,
    configurable: true,
    get() {
      console.log('price属性被读取了')
      depDemo.depend();
      return value
    },
    set(newVal) {
      console.log('price属性被修改了')
      value = newVal;
      depDemo.notify();
    }
  });

  obj.price = 3000; // price属性被读取了
  var price = obj.price; // price属性被读取了

  // ...............................................................................
  //  2、完整案例
  // 类组成
  // Observer :观察者对象,将一个对象的所有属性变成get和set访问器模式。
  // Dep : 通过一个subs数组维护依赖对象,主要有depend添加依赖,notify通知所有依赖变更。
  // Watcher :监听对象,主要有get方法获取属性,添加自身到dep中,update方法为notify通知的变更

  // 备注:一个Observer对象对应一个子属性,并且对应了一个Dep对象,而Dep可以添加多个Watcher对象
  // 主要流程:当一个Watcher对象使用get方法获取属性,把自身设置成全局对象,最终调用的是Observer的get方法
  // 此时Observer会把Watcher添加到Dep对象中,而这个属性变更,又会由Observer的set到Watcher的update变更。
 
  // Observer
  class Observer {
    constructor(value) {
      this.value = value;
      if(Array.isArray(value)) {
      }else {
        this.walk(value)
      }
    }
    walk(obj) {
      const keys = Object.keys(obj);
      for (let i = 0; i < keys.length; i++) {
         defineReactive(obj,keys[i]);
      }
    }
  }
  function defineReactive(obj, key, val) {
    if (arguments.length === 2) {
      val = obj[key];
    }
    if(typeof val === 'object') {
      new Observer(val); // 递归操作
    }
    const dep = new Dep()
    Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: true,
      get() {
        dep.depend();
        return val;
      },
      set(newVal) {
        if(val===newVal) {
          return
        }
        val = newVal;
        dep.notify();
      }
    });
  }

  // Dep依赖收集
  class Dep {
    constructor() {
      this.subs = [];
    }
    addSub(sub) {
      this.subs.push(sub);
    }
    removeSub(sub) {
     remove(this.subs, sub)
    }
    // --当依赖调用者获取属性时,会把自身设置到window.target属性
    depend () {
     if (window.target) {
       this.addSub(window.target)
     }
   }
   notify() {
     console.log('更新操作')
     const subs = this.subs;
     for (let i = 0, l = subs.length; i < l; i++) {
       subs[i].update()
     }
   }
}

function remove (arr, item) {
  if (arr.length) {
    const index = arr.indexOf(item)
    if (index > -1) {
      return arr.splice(index, 1)
    }
  }
}

// Watcher对象
class Watcher {
  constructor (vm, expOrFn, cb) {
    this.vm = vm;
    this.cb = cb;
    this.getter = parsePath(expOrFn)
    this.value = this.get()
  }
  get () {
    window.target = this;
    const vm = this.vm
    let value = this.getter.call(vm, vm)
    window.target = undefined;
    return value
  }
  update () {
    const oldValue = this.value
    this.value = this.get()
    this.cb.call(this.vm, this.value, oldValue);
  }
}

const bailRE = /[^\w.$]/
function parsePath (path) {
  if (bailRE.test(path)) {
    return
  }
  const segments = path.split('.');
  return function (obj) {
    for (let i = 0; i < segments.length; i++) {
      if (!obj) return
      obj = obj[segments[i]]
    }
    return obj
  }
}

// 定义window来全局临时挂载target属性。
var window = {}
var vue = {
  id:'id',
  name:'name',
}
new Observer(vue);
new Watcher(vue, 'id', (val, oldValue)=>{
    console.log(val, oldValue);
});
vue.id = 2;

 

Supongo que te gusta

Origin blog.csdn.net/shuixiou1/article/details/112689686
Recomendado
Clasificación