• 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
        });
      }
  • 相关阅读:
    CCF201712-2游戏
    SpringMVC(未完待续....)
    MySQL----商品表及商品分类表例子
    Spring----属性注入的三种方式
    Spring的配置文件applicationContext.xml
    Spring----getBean的四种用法
    Spring----工厂模式
    spring第一个小例子(Spring_xjs1)
    JSON
    XStream(可把JavaBean转换成XML的小工具)
  • 原文地址:https://www.cnblogs.com/the-last/p/11525483.html
Copyright © 2020-2023  润新知