1. 程式人生 > >vue3.0後的學習proxy筆記(覺得和reflect一起對比學習效果更佳)

vue3.0後的學習proxy筆記(覺得和reflect一起對比學習效果更佳)

      最近看相關報道,vue3.0 開發計劃 傳送門 https://juejin.im/post/5bb719b9f265da0ab915dbdd

其中,監測機制:

一句話介紹:更加全面、精準、高效;更具可除錯性的響應跟蹤;以及可用來建立響應式物件的 API。

3.0 將帶來一個基於 Proxy 的 observer 實現,它可以提供覆蓋語言 (JavaScript——譯註) 全範圍的響應式能力,消除了當前 Vue 2 系列中基於 Object.defineProperty 所存在的一些侷限,如:

  • 對屬性的新增、刪除動作的監測
  • 對陣列基於下標的修改、對於 .length 修改的監測
  • 對 Map、Set、WeakMap 和 WeakSet 的支援

另外這個新的 observer 還有以下特性:

  • 公開的用於建立 observable (即響應式物件——譯註) 的 API。這為小型到中型的應用提供了一種輕量級的、極其簡單的跨元件狀態管理解決方案。(譯註:在這之前我們可以通過另外 new Vue({data : {...}}) 來建立這裡所謂的 observable;另外,其實 vuex 內部也是用這種方式來實現的)
  • 預設為惰性監測(Lazy Observation)。在 2.x 版本中,任何響應式資料,不管它的大小如何,都會在啟動的時候被監測。如果你的資料量很大的話,在應用啟動的時候,這就可能造成可觀的效能消耗。而在 3.x 版本中,只有應用的初始可見部分所用到的資料會被監測,更不用說這種監測方案本身其實也是更加快的。
  • 更精準的變動通知。舉個例子:在 2.x 系列中,通過 Vue.set 強制新增一個新的屬性,將導致所有依賴於這個物件的 watch 函式都會被執行一次;而在 3.x 中,只有依賴於這個具體屬性的 watch 函式會被通知到。
  • 不可變監測物件(Immutable observable):我們可以建立一個物件的“不可變”版本,以此來阻止對他的修改——包括他的巢狀屬性,除非系統內部臨時解除了這個限制。這種機制可以用來凍結傳遞到元件屬性上的物件和處在 mutation 範圍外的 Vuex 狀態樹。
  • 更良好的可除錯能力:通過使用新增的 renderTrackedrenderTriggered
    鉤子,我們可以精確地追蹤到一個元件發生重渲染的觸發時機和完成時機,及其原因

那麼 proxy 到底是什麼 該怎麼使用呢???

target是指代理的原物件,它是你需要攔截訪問的原始物件,它總是作為Proxy構造器的第一個方法,也可以傳遞到每個trap中。

handler是一個包含你要進行攔截和處理的物件,也就是你攔截了原始物件以後想幹什麼?主要的代理內容在這裡,是作為Proxy構造器的第二個方法引數傳統,它是實現Proxy API。

trap用來規定對於指定什麼方法進行攔截處理,如果你想攔截get方法的呼叫,那麼你要定義一個get trap。

let  obj = { name:"bruce",age:"25"}

let handler = {
//get運算子有兩個引數 - 物件本身和被訪問的屬性。
    get:function(target,prop){
       console.log('target:'+target+"prop:"+prop);
    },
//set操作符有三個引數 - 物件本身,被訪問的屬性和為該屬性設定的值。
    set:function(target,prop,value){
       if(typeof(value) == 'string'){ //用來作 型別檢驗
    
       }
       console.log('target:'+target+"prop:"+prop+"value:"+value);
    }
}

let proxy = new Proxy(obj,handler)

proxy.name //列印 target:[object Object]prop:name

proxy.name = 333 //列印 target:[object Object]prop:namevalue:333

    proxy與設計模式

     在面向物件的程式設計中,代理模式的合理使用能夠很好的體現下面兩條原則:

  • 單一職責原則: 面向物件設計中鼓勵將不同的職責分佈到細粒度的物件中,Proxy 在原物件的基礎上進行了功能的衍生而又不影響原物件,符合鬆耦合高內聚的設計理念。

  • 開放-封閉原則:代理可以隨時從程式中去掉,而不用對其他部分的程式碼進行修改,在實際場景中,隨著版本的迭代可能會有多種原因不再需要代理,那麼就可以容易的將代理物件換成原物件的呼叫

    Proxy實現前端中3種代理模式的使用場景,分別是:快取代理驗證代理實現私有屬性

1.0.0 快取代理

//計算斐波那契數列  40以上很慢
const getFib = (number) => {
  if (number <= 2) {
    return 1;
  } else {
    return getFib(number - 1) + getFib(number - 2);
  }
}

//建立快取代理的工廠函式

const getCacheProxy = (fn, cache = new Map()) => {
  return new Proxy(fn, {
    apply(target, context, args) {
      const argsString = args.join(' ');
       console.log(args)
      if (cache.has(argsString)) {
        console.log(argsString)
        // 如果有快取,直接返回快取資料
        console.log(`輸出${args}的快取結果: ${cache.get(argsString)}`);
        return cache.get(argsString);
      }
      const result = fn(...args);
      cache.set(argsString, result);
      return result;
    }
  })
}

//使用
const getFibProxy = getCacheProxy(getFib)
getFibProxy(40); // 102334155
getFibProxy(40); // 輸出40的快取結果: 102334155


 

1.0.1 驗證代理

// 表單物件

const userForm = {
  account: '',
  password: '',
}

// 驗證方法
const validators = {
  account(value) {
    // account 只允許為中文
    const re = /^[\u4e00-\u9fa5]+$/;

    return {
      valid: re.test(value),
      error: '"account" is only allowed to be Chinese'
    }
  },

  password(value) {
    // password 的長度應該大於6個字元
    return {
      valid: value.length >= 6,
      error: '"password "should more than 6 character'
    }
  }
}


// 校驗器
const getValidateProxy = (target, validators) => {

  return new Proxy(target, {

    _validators: validators,

    set(target, prop, value) {

      if (value === '') {

        console.error(`"${prop}" is not allowed to be empty`);

        return target[prop] = false;

      }

      const validResult = this._validators[prop](value);

      if(validResult.valid) {

        return Reflect.set(target, prop, value);

      } else {

        console.error(`${validResult.error}`);

        return target[prop] = false;

      }

    }

  })

}
// 使用
const userFormProxy = getValidateProxy(userForm, validators);
userFormProxy.account = '123'; // "account" is only allowed to be Chinese
userFormProxy.password = 'he'; // "password "should more than 6 character

 

1.0.2 實現私有屬性 (設定訪問限制)

 

function getPrivateProps(obj, filterFunc) {

  return new Proxy(obj, {
    get(obj, prop) {
      if (!filterFunc(prop)) {
        let value = Reflect.get(obj, prop);
        // 如果是方法, 將this指向修改原物件
        if (typeof value === 'function') {
          value = value.bind(obj);
        }
        return value;
      }
    },

    set(obj, prop, value) {

      if (filterFunc(prop)) {
        throw new TypeError(`Can't set property "${prop}"`);
      }
      return Reflect.set(obj, prop, value);
    },

    has(obj, prop) {
      return filterFunc(prop) ? false : Reflect.has(obj, prop);
    },

    ownKeys(obj) {
      return Reflect.ownKeys(obj).filter(prop => !filterFunc(prop));
    },

    getOwnPropertyDescriptor(obj, prop) {
      return filterFunc(prop) ? undefined : Reflect.getOwnPropertyDescriptor(obj, prop);
    }

  });

}
//因為私有屬性 一般以 _開頭 這裡就簡單過濾
function propFilter(prop) {
  return prop.indexOf('_') === 0;
}

 

 

參考資料:https://www.imooc.com/article/47896

http://es6.ruanyifeng.com/#docs/proxy 

https://www.cnblogs.com/diligenceday/p/5474126.html