從vue源碼來看Proxy的用途

從vue源碼來看Proxy的用途

The Proxy object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc).MDN Proxyvue


MDN表述該對象構造器是用於對某對象定義用戶自定義行爲的。那麼,這究竟是什麼意思呢?咱們下面直接來分析一下Vue源碼中應用到Proxy對象的地方,來理解該對象構造器的做用。react

Proxy在Vue中的應用

首先咱們來看一看Vue源碼的 1430行git

/* not type checking this file because flow doesn't play well with Proxy */

  var initProxy;

  {
    var allowedGlobals = makeMap(
      'Infinity,undefined,NaN,isFinite,isNaN,' +
      'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' +
      'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' +
      'require' // for Webpack/Browserify
    );

    var warnNonPresent = function(target, key) {
      warn(
        "Property or method \"" + key + "\" is not defined on the instance but " +
        "referenced during render. Make sure to declare reactive data " +
        "properties in the data option.",
        target
      );
    };

    var hasProxy = typeof Proxy !== 'undefined' &&
    Proxy.toString().match(/native code/);

    if (hasProxy) {
      var isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta');
      config.keyCodes = new Proxy(config.keyCodes, {
        set: function set(target, key, value) {
          if (isBuiltInModifier(key)) {
            warn(("Avoid overwriting built-in modifier in config.keyCodes: ." + key));
            return false
          } else {
            target[key] = value;
            return true
          }
        }
      });
    }

    var hasHandler = {
      has: function has(target, key) {
        var has = key in target;
        var isAllowed = allowedGlobals(key) || key.charAt(0) === '_';
        if (!has && !isAllowed) {
          warnNonPresent(target, key);
        }
        return has || !isAllowed
      }
    };

    var getHandler = {
      get: function get(target, key) {
        if (typeof key === 'string' && !(key in target)) {
          warnNonPresent(target, key);
        }
        return target[key]
      }
    };

    initProxy = function initProxy(vm) {
      if (hasProxy) {
        // determine which proxy handler to use
        var options = vm.$options;
        var handlers = options.render && options.render._withStripped
          ? getHandler
          : hasHandler;
        vm._renderProxy = new Proxy(vm, handlers);
      } else {
        vm._renderProxy = vm;
      }
    };
  }

首先,咱們要明白一件事情,vue放棄了對ie9如下的支持。即便這樣,因爲Proxy做爲一個es6的新特性,支持度依然不高,做者也只是嘗試着使用了一下。下面咱們來一步一步講解。es6

源碼講解

var allowedGlobals = makeMap(
      'Infinity,undefined,NaN,isFinite,isNaN,' +
      'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' +
      'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' +
      'require' // for Webpack/Browserify
    );

該處定義了所容許的全局對象類型。github


var warnNonPresent = function(target, key) {
      warn(
        "Property or method \"" + key + "\" is not defined on the instance but " +
        "referenced during render. Make sure to declare reactive data " +
        "properties in the data option.",
        target
      );
 };

該處定義了一個報警方法,傳入鍵名或方法名,log顯示一條警告app


var hasProxy = typeof Proxy !== 'undefined' &&
    Proxy.toString().match(/native code/);

該處是對es6特性Proxy的檢測, 其檢測手段是確認Proxy是原生實現並未被用戶代碼所覆蓋。函數


var isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta');
    config.keyCodes = new Proxy(config.keyCodes, {
        set: function set(target, key, value) {
          if (isBuiltInModifier(key)) {
            warn(("Avoid overwriting built-in modifier in config.keyCodes: ." + key));
            return false
          } else {
            target[key] = value;
            return true
          }
        }
      });

該段代碼對config.keyCodes對象進行了代理,其意義在於禁止用戶修改Vue內建的一些按鍵值,這些按鍵值和按鍵名是對應的。若是用過Vue的用戶應該知道,Vue在事件對象上對一些經常使用按鍵和經常使用操做進行了內建(這個內建過程被用eval函數壓縮了,由一堆代碼段構成的,沒什麼看頭),做者確定不但願也不容許用戶修改配置的時候覆蓋了內建內容。因而,當咱們作config.keyCodes['stop'] = xxx這樣的操做的時候,Vue就會告訴你說「你這我的,不老實,爲何想着改我東西」,直接打出禁止改寫內建配置的警告。若是非內建內容,那麼能夠直接設置上。ui


initProxy = function initProxy(vm) {
      if (hasProxy) {
        // determine which proxy handler to use
        var options = vm.$options;
        var handlers = options.render && options.render._withStripped
          ? getHandler
          : hasHandler;
        vm._renderProxy = new Proxy(vm, handlers);
      } else {
        vm._renderProxy = vm;
      }
    };

這句話就再也不解釋了,相似上面。不過讀者可能會問has是什麼? 那麼下面咱們來說解一下Proxy的各個參數this

Proxy所需參數

兩個, var b = new Proxy(a, { has: fn xxx, get: fn xxx, set: fn xxx .... })代理

target 被代理的對象

handler 處理器對象

用來定義咱們對該對象的各類操做

完整的handler處理器對象內容:

{
    get: '咋獲取',
    set: '咋設置',
    deleteProperty: '咋刪除',
    enumerate: '咋枚舉',
    ownKeys: '咋獲取全部該對象的屬性鍵 ',
    has: '問你有沒有, 好比 "xxx" in target',
    defineProperty: '如何defineProperty, 這個咱們也是能夠代理的',
    getOwnPropertyDescriptor: '獲取屬性描述的代理',
    getPrototypeOf: '找原型時候的代理',
    setPrototypeOf: '設置對象原型的時候的代理',
    isExtensible: '判斷對象是否可擴展的時候的代理',
    preventExtensions: '設置阻止對象擴展的時候的代理',
    apply: '執行調用操做的時候的代理',
    construct: '執行實例化的時候的代理'
}

以上皆是函數~~

因此Proxy的做用是?

攔截,預警,上報,擴展功能,統計,強化對象...能想獲得的都能沾到點邊,這裏Vue的代碼主要將代理運用於攔截。而且因爲規範依然在發展,因此你們慎用。。。

資料

MDN js Proxy Object

MDN Proxy Handler

sec-proxy-object-internal-methods-and-internal-slots

相關文章
相關標籤/搜索