Vue setter/getter 是何原理?

1 、 defineProperty 重定义对象

JS原生es5版本提供对象重新定义的接口 defineProperty 

defineProperty 可以修改对象的访问器属性,对象属性值发生变化前后可以触发回调函数。

对象的访问器属性包括 2 种类型:数据描述符、 存取描述符

 1.1 数据描述符
value:对象key的值,默认是 空字符串 ''
writeable:是否可写,默认 true
configurable:true是否可配置,默认 true
enumerable:true是否可枚举, 默认 true

Object.getOwnPropertyDescriptors(obj);
{
    k:{
      configurable: true,
      enumerable: true,
      value: 90,
      writable: true
    }
}

 

 1.2 存取描述符

set:function(){}属性访问器 进行写操作时调用该方法
get:function(){}属性访问器 进行读操作时调用该方法
属性描述符: configurable 、enumerable
configurable 、enumerable、 set 、 get

对象中新增key的value发生变化时会经过set和get方法

var obj = {};
var
temp = ''; Object.defineProperty(obj, 'name', { configurable: true, enumerable: true, get: function () { return temp; }, set: function (newValue) { temp = newValue } }); // 需要维护一个可访问的变量 temp

或写在 obj对象内,如下:

 var obj = {
       tempValue: 'duyi',
       get name () {
            return this.tempValue;
       },
       set name (newValue) {
            this.tempValue = newValue;
       }
 };

 obj.name = 10;
 console.log( obj.name ); // 10

 小结一次:到这里来基本上知道 getter setter是可以实现的,基于这个简单理论作出一个复杂逻辑。

 

2 、Observer 源码

  /**
   * Observer类方法将对象修改为可被观察。
   * 一旦应用了这个类方法, 对象的每一个key会被转换成 getter/setter
   * 用于收集依赖项和触发更新。
   */
  var Observer = function Observer (value) {
    this.value = value;          // 保存被观察的值到方法的实例属性
    this.dep = new Dep();        // 建立一个Dep实例
    this.vmCount = 0;            // vmCount 记录vm结构的个数
    def(value, '__ob__', this);  // value 对象 增加 ‘__ob__’ key,并赋值 this
    if (Array.isArray(value)) {  // value 对象如果是数组
      if (hasProto) {            // 表示在[]上可以使用 __proto__ 
protoAugment(value, arrayMethods); // 将arrayMethods 添加到value的__proto__。arrayMethods 好像是重写了数组的一部分原生方法,后面再看 } else { copyAugment(value, arrayMethods, arrayKeys); // 说不定他不支持 __proto__ ,调用def方法增强对象。 } this.observeArray(value); // 然后将这个增强后的数组,每一项都执行observe } else { this.walk(value); // walk 在Observer的原型上,对象转换为 getter setter。 } }; /** * 遍历所有属性将它们转换为 getter/setter,仅当值类型为对象时调用。 */ Observer.prototype.walk = function walk (obj) { var keys = Object.keys(obj); for (var i = 0; i < keys.length; i++) { defineReactive$$1(obj, keys[i]); // defineReactive$$1 () 方法,这个方法才是实现 getter / setter 的原方法!!! } }; /** * 观察数组项列表 */ Observer.prototype.observeArray = function observeArray (items) { for (var i = 0, l = items.length; i < l; i++) { observe(items[i]); // observe 方法 } }; /** * 截获原型链使用 __proto__ 的方式来 * 增强一个目标的对象或数组,简称原型增强。 */ function protoAugment (target, src) { /* eslint-disable no-proto */ target.__proto__ = src; /* eslint-enable no-proto */ } /** * 增加对象原型properties */ function copyAugment (target, src, keys) { for (var i = 0, l = keys.length; i < l; i++) { var key = keys[i]; def(target, key, src[key]); } } /**
* 在Observer方法的原型属性 observerArray 上有用到!
* 大概意思是创建可观察实例,返回可观察实例或已有的可观察对象。
*/ function observe (value, asRootData) {

// 如果不是object时,或是 VNode 的实例不进行 observe
if (!isObject(value) || value instanceof VNode) { return } var ob; if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { ob = value.__ob__; } else if ( shouldObserve && !isServerRendering() && (Array.isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value._isVue ) { ob = new Observer(value); } if (asRootData && ob) { ob.vmCount++; } return ob } /** * 这里是实现getter setter的关键代码
* 这里是实现getter setter的关键代码
* 这里是实现getter setter的关键代码
*
* 在对象上定义一个 有反应的原型
* 传参:obj对象,key关键字,val值,customSetter在set的时候会被执行(第4个参数),shallow 默认为undefined,为 true 时不执行 observe 函数。
*/
  function defineReactive$$1 (
    obj,
    key,
    val,
    customSetter,
    shallow
  ) {
    var dep = new Dep();

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

    // 预定义的getter和setter
    var getter = property && property.get;
    var setter = property && property.set;
    if ((!getter || setter) && arguments.length === 2) {
      val = obj[key];
    }

    var childOb = !shallow && observe(val);
    Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: true,
      get: function reactiveGetter () {
        var value = getter ? getter.call(obj) : val;
        if (Dep.target) {
          dep.depend();
          if (childOb) {
            childOb.dep.depend();
            if (Array.isArray(value)) {
              dependArray(value);
            }
          }
        }
        return value
      },
      set: function reactiveSetter (newVal) {
        var 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 (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();
      }
    });
  }

 

 订阅observe对象的变化,通知触发update,参考订阅-发布模式。

  /**
   * Dep 是可以有多个指令订阅的可观察对象,目的就是对一个目标深层处理
   */
  
  var uid = 0;
  var Dep = function Dep () {
    this.id = uid++;    //  添加了2个实例属性,id 用于排序和 subs 数组统计sub
    this.subs = [];
  };
  
  // 在 subs中添加 sub
  Dep.prototype.addSub = function addSub (sub) {
    this.subs.push(sub);
  };
  
  // 从 subs中移除 sub
  Dep.prototype.removeSub = function removeSub (sub) {
    remove(this.subs, sub);
  };


  Dep.prototype.depend = function depend () {
    if (Dep.target) {
      Dep.target.addDep(this);  // addDep 是 Watcher 的原型方法,用于指令增加依赖
    }
  };

  Dep.prototype.notify = function notify () {
    // 稳定订阅列表
    var subs = this.subs.slice();
    if (!config.async) {
      // 如果不是在异步运行,在程序调度中 subs 不可以被排序!
      // 然后排序以确保正确的顺序。
      subs.sort(function (a, b) { return a.id - b.id; });
    }
    for (var i = 0, l = subs.length; i < l; i++) {
      // 然后顺序触发 Watcher 原型的 update 方法
      subs[i].update();
    }
  };

  // 当前目标程序被评估,这个评估全局唯一,一次只要一个观察者可以被评估
  Dep.target = null;
  var targetStack = [];

  // 将目标程序推送到目标栈
  function pushTarget (target) {
    targetStack.push(target);
    Dep.target = target;
  }
  // 执行出栈先去掉
  function popTarget () {
    targetStack.pop();
    Dep.target = targetStack[targetStack.length - 1];
  }

 

 

 全局def方法,定义对象。

  // def 方法比较简单
  /**
   * 定义一个原型
   * obj 
   * key 对象的key关键字
   * val  对象的value值
   * 是否可枚举,默认可写可配置
   */

  function def (obj, key, val, enumerable) {
    Object.defineProperty(obj, key, {
      value: val,
      enumerable: !!enumerable,
      writable: true,
      configurable: true
    });
  }

 

posted @ 2019-09-17 17:40  kimoon  阅读(4298)  评论(0编辑  收藏  举报