首页 > 其他 > 详细

Vue setter/getter 是何原理?

时间:2019-09-17 17:53:58      阅读:310      评论:0      收藏:0      [点我收藏+]

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

修改后的 get/set 不使用 defineProperty 方法,可以写在 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__,只有此处用到,这还有不能用的地方么,nodejs也可以。 
        protoAugment(value, arrayMethods);  // 将arrayMethods 放在value的__proto__ 原型上。arrayMethods这个方法有点深,好像是重写了数组的一部分原型方法
      } else {
        copyAugment(value, arrayMethods, arrayKeys);  // 说不定真不支持 __proto__ ,调用def方法拷贝增强对象。
      }
      this.observeArray(value);
    } else {
      this.walk(value);
    }
  };

  /**
   * 遍历所有属性将它们转换为 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 方法
    }
  };

  // 只在 Observer有用到此方法
  /**
   * 截获原型链使用 __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]);
    }
  }

  /**
   * Attempt to create an observer instance for a value,
   * returns the new observer if successfully observed,
   * or the existing observer if the value already has one.
   */
  function observe (value, asRootData) {
    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();
      }
    });
  }

 

 

 

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

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

 

Vue setter/getter 是何原理?

原文:https://www.cnblogs.com/the-last/p/11525483.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!