Object.defineProperty的缺點及vue3爲何用proxy

這是我以前被問到的一個問題,被問以前沒有想過,被問到的時候很懵😯 vue2的數據雙向綁定是用的這個Object.defineProperty,vue3會用proxy實現數據劫持。那vue3爲何會這麼作呢?vue

vue數據雙向綁定的實現原理

這個問題我以前試着描述過不少次,可是由於都自認爲不夠清楚。 vue是利用數據劫持結合發佈訂閱模式實現的數據雙向綁定。在vue的實現裏面,簡單來講有四部分:數組

  • mvvm用來初始化數據
  • observer用來對初始數據經過Object.defineProperty添加setter和getter,當取數據(即調用get)的時候添加訂閱對象(watcher)到數組裏, 當給數據賦值(即調用set)的時候就能知道數據的變化,此時調用發佈訂閱中心的notify,從而遍歷當前這個數據的訂閱數組,執行裏面全部的watcher,通知變化update。
  • compiler是用來把data編譯到dom中。分三步:1.先把真實的dom移入到內存中 fragment,2.編譯:提取想要的元素節點v-model和文本節點{{}};3.把編譯好的fragment塞回到頁面去。第二步驟中會對編譯到dom中的data添加watcher,當data變化時,這裏的watcher回調也能收到通知獲得執行。
  • watcher是oberver和compiler之間通訊的橋樑。

Object.defineProperty的缺點

1.Object.defineProperty的第一個缺陷,沒法監聽數組變化。 可是vue中是能夠監聽數組的變化的,那他是怎麼實現的呢?使用瞭如下八種方法:瀏覽器

push()
pop()
shift()
unshift()
splice()
sort()
reverse()
複製代碼

實現示例參考:性能優化

const aryMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'];
const arrayAugmentations = [];

aryMethods.forEach((method)=> {

    // 這裏是原生Array的原型方法
    let original = Array.prototype[method];

   // 將push, pop等封裝好的方法定義在對象arrayAugmentations的屬性上
   // 注意:是屬性而非原型屬性
    arrayAugmentations[method] = function () {
        console.log('我被改變啦!');

        // 調用對應的原生方法並返回結果
        return original.apply(this, arguments);
    };

});

let list = ['a', 'b', 'c'];
// 將咱們要監聽的數組的原型指針指向上面定義的空數組對象
// 別忘了這個空數組的屬性上定義了咱們封裝好的push等方法
list.__proto__ = arrayAugmentations;
list.push('d');  // 我被改變啦! 4

// 這裏的list2沒有被從新定義原型指針,因此就正常輸出
let list2 = ['a', 'b', 'c'];
list2.push('d');  // 4
複製代碼

以上代碼參考:這裏bash

2.Object.defineProperty的第二個缺陷,只能劫持對象的屬性,所以咱們須要對每一個對象的每一個屬性進行遍歷,若是屬性值也是對象那麼須要深度遍歷,顯然能劫持一個完整的對象是更好的選擇。app

vue3爲何用proxy?

1.proxy能夠直接監聽數組的變化; 2.proxy能夠監聽對象而非屬性.它在目標對象以前架設一層「攔截」,外界對該對象的訪問,都必須先經過這層攔截,所以提供了一種機制,能夠對外界的訪問進行過濾和改寫。 Proxy直接能夠劫持整個對象,並返回一個新對象。dom

總結

  • Proxy返回的是一個新對象,咱們能夠只操做新的對象達到目的,而Object.defineProperty只能遍歷對象屬性直接修改;
  • Proxy做爲新標準將受到瀏覽器廠商重點持續的性能優化,也就是傳說中的新標準的性能紅利
  • 固然,Proxy的劣勢就是兼容性問題,並且沒法用polyfill實現

本文參考:參考資料1mvvm

相關文章
相關標籤/搜索