1. 程式人生 > 程式設計 >vue中defineProperty和Proxy的區別詳解

vue中defineProperty和Proxy的區別詳解

Proxy的出現,給vue響應式帶來了極大的便利,比如可以直接劫持陣列、物件的改變,可以直接新增物件屬性,但是相容性可能會有些問題

Proxy可以劫持的陣列的改變,defineProperty 需要變異

defineProperty 中劫持陣列變化的變異的方法

可以理解為在陣列例項和原型之間,插入了一個新的原型的物件,這個原型方法實現了變異的方法,也就真正地攔截了陣列原型上的方法

我們來看下vue2.x的原始碼

// vue 2.5.0
var arrayProto = Array.prototype;
var arrayMethods = Object.create(arrayProto); // Array {}
function def(obj,key,val,enumerable) {
  Object.defineProperty(obj,{
   value: val,enumerable: !!enumerable,writable: true,configurable: true
  });
}
var methodsToPatch = [
  'push','pop','shift','unshift','splice','sort','reverse'
  ];

  /**
  * Intercept mutating methods and emit events
  */
  methodsToPatch.forEach(function(method) {
  // cache original method
  var original = arrayProto[method]; 
    // 比如 method是push,則結果為
    // ƒ push() { [native code] }
  def(arrayMethods,method,function mutator() {
   var args = [],len = arguments.length;
   while (len--) args[len] = arguments[len];

   var result = original.apply(this,args);
   var ob = this.__ob__;
   var inserted;
   switch (method) {
   case 'push':
   case 'unshift':
    inserted = args;
    break
   case 'splice':
    inserted = args.slice(2);
    break
   }
   if (inserted) {
   ob.observeArray(inserted);
   }
   // notify change
   ob.dep.notify();
   return result
  });
  });
  
  /**
  * Observe a list of Array items.
  */
  Observer.prototype.observeArray = function observeArray(items) {
  for (var i = 0,l = items.length; i < l; i++) {
   observe(items[i]); // 後續的邏輯
  }
  };

Proxy可以直接劫持陣列的改變

  let proxy = new Proxy(fruit,{
    get: function (obj,prop) {
      return prop in obj ? obj[prop] : undefined

    },set: function (obj,prop,newVal) {
      obj[prop] = newVal
      console.log("newVal",newVal) // 輸出{ name: "lemon",num: 999 }
      return true;
    }
  })
  proxy.push({ name: "lemon",num: 999 })
  console.log(fruit)

vue中defineProperty和Proxy的區別詳解

Proxy代理可以劫持物件的改變,defineProperty需要遍歷

defineProperty

   let fruit = {
     "apple": 2,"pear": 22,"peach": 222
  }
  Object.keys(fruit).forEach(function (key) {
      Object.defineProperty(fruit[i],{
        enumerable: true,configurable: true,get: function () {
          return val;

        },set: function (newVal) {
          val = newVal; // 輸出 newVal 888
          console.log("newVal",newVal)
        }
      })
    })
   fruit.apple = 888

Proxy

   let fruit = {
     "apple": 2,"peach": 222
  }
  let proxy = new Proxy(fruit,newVal) // 輸出 newVal 888
      return true;
    }
  })
  proxy.apple = 888

Proxy代理可以劫持物件屬性的新增,defineProperty用this.$set來實現
defineProperty,如果屬性不存在,則需要藉助this.$set

<div id="app">
  <span v-for="(value,name) in fruit">{{name}}:{{value}}個 </span> 
  <button @click="add()">新增檸檬</button>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
  new Vue({
    el: '#app',data() {
      return {
        fruit: {
          apple: 1,banana: 4,orange: 5 
        }
      }
    },methods: {
      add() {
          this.fruit.lemon = 5;  // 不會讓檢視發生變化
        // this.$set(this.fruit,"lemon",5)  // this.$set可以
      }
    }
  })
</script>

  Object.keys(fruit).forEach(function (key) {
    Object.defineProperty(fruit,{
      enumerable: true,get: function () {
        return val;

      },set: function (newVal) {
        val = newVal;
        console.log("newVal",newVal) // 根本沒有進去這裡
      }
    })
  })

Proxy 直接可以新增屬性

// vue 3
<div id="app">
  <span v-for="(value,name) in fruit">{{name}}:{{value}}個 </span>
  <button @click="add()">新增檸檬</button>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
  Vue.createApp({
    data() {
      return {
        fruit: {
          apple: 1,orange: 5
        }
      }
    },methods: {
      add() {
        this.fruit.lemon = 5; // 這樣子是可以的
      }
    }
  }).mount('#app') // vue 3 不再是使用el屬性,而是使用mount
</script>

  let proxy = new Proxy(fruit,newVal) // lemon,888
      return true;
    }
  })
  proxy.lemon = 888

Proxy

其他屬性

vue中defineProperty和Proxy的區別詳解

應用場景 promisify化

用Proxy寫一個場景,請求都是通過回撥,如果我們需要用promise包一層的話,則可以

// server.js
// 假設這裡都是回撥
export const searchResultList = function (data,callback,errorCallback) {
 axios.post(url,data,errorCallback)
}
// promisify.js
import * as server from './server.js'
const promisify = (name,obj) => (option) => {
 return new Promise((resolve,reject) => {
  return obj[name](
   option,resolve,reject,)
 })
}
const serverPromisify = new Proxy(server,{
 get (target,prop) {
  return promisify(prop,server)
 }
})
export default serverPromisify

使用

// index.js
import serverPromisify from './serverPromisify'
serverPromisify.searchResultList(data).then(res=>{

})

如有不正確,望請指出

vue中defineProperty和Proxy的區別詳解

留下一個疑問,既然相容性不是很好,那麼尤大是怎麼處理polyfill呢

到此這篇關於vue中defineProperty和Proxy的區別詳解的文章就介紹到這了,更多相關vue defineProperty和Proxy內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!