Vue3.0 響應式數據原理:ES6 Proxy

Vue3.0 開始用 Proxy 代替 Object.defineProperty了,這篇文章結合實例教你如何使用Proxyjavascript

本篇文章同時收錄【前端知識點】中,連接直達html

閱讀本文您將收穫

  • JavaScript 中的 Proxy 是什麼?能幹什麼?
  • Vue3.0 開始爲何用 Proxy 代替 Object.defineProperty

Proxy 是什麼

解釋參考MDN,連接直達前端

名詞解釋

  • Proxy 對象用於定義基本操做的自定義行爲(如屬性查找、賦值、枚舉、函數調用等)
  • Proxy 用於修改某些操做的默認行爲,也能夠理解爲在目標對象以前架設一層攔截,外部全部的訪問都必須先經過這層攔截,所以提供了一種機制,能夠對外部的訪問進行過濾和修改

語法

  • const p = new Proxy(target, handler)
    • target: 要使用 Proxy 包裝的目標對象(能夠是任何類型的對象,包括原生數組,函數,甚至另外一個代理)
    • handler: 對該代理對象的各類操做行爲處理(爲空對象的狀況下,基本能夠理解爲是對第一個參數作的一次淺拷貝)
  • 簡而言之:target 就是你想要代理的對象;而 handler 是一個函數對象,其中定義了全部你想替 target 代爲管理的操做對象,包含了:
    • *handler.has(target, prop): in 操做符的捕捉器,攔截HasProperty操做
    • *handler.get(target, prop): 屬性讀取操做的捕捉器
    • *handler.set(target, prop, value): 屬性設置操做的捕捉器
    • *handler.apply(target, object, args): 函數調用操做的捕捉器,攔截函數的調用、call和apply操做
    • handler.getPrototypeOf(): Object.getPrototypeOf 方法的捕捉器
    • handler.setPrototypeOf(): Object.setPrototypeOf 方法的捕捉器
    • handler.isExtensible(): Object.isExtensible 方法的捕捉器
    • handler.preventExtensions(): Object.preventExtensions 方法的捕捉器
    • handler.getOwnPropertyDescriptor(): Object.getOwnPropertyDescriptor 方法的捕捉器
    • handler.defineProperty(): Object.defineProperty 方法的捕捉器
    • handler.deleteProperty(): delete 操做符的捕捉器
    • handler.ownKeys(): Object.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法的捕捉器
    • handler.construct(): new 操做符的捕捉器
  • 注意:若是一個屬性不可配置 || 不可寫,則該屬性不可被代理,經過 Proxy 訪問該屬性會報錯。
  • * 標記的trap爲本文都要涉及到的

Proxy 能幹什麼?

當你想進行如下操做時proxy模式一般會頗有用:

  • 攔截或控制對某個對象的訪問
  • 經過隱藏事務或輔助邏輯來減少方法/類的複雜性
  • 防止在未經驗證/準備的狀況下執行重度依賴資源的操做

一:javascript中真正的私有變量/攔截has...in...操做/給出提示信息或是阻止特定操做

傳統方法私有變量可獲取可修改
Proxy 設置私有變量
  • 針對私有變量,可使用一個proxy來截獲針對某個屬性的請求並做出限制或是直接返回 undefined
  • 還可使用 has trap 來掩蓋這個屬性的存在
  • has 方法攔截的是 hasProperty 操做,不是 hasOwnProperty,因此 has...in 方法不判斷一個屬性是自身屬性仍是繼承的屬性vue

  • 注意: has...in 能夠攔截到,for...in 攔截不到java

  • 阻止其餘人刪除屬性,想讓調用方法的人知道該方法已經被廢棄,或是想阻止其餘人修改屬性git

  • 注意: 要是 Proxy 代理起做用,必須針對 Proxy 的實例進行操做,而不是針對目標對象進行操做

二:數據校驗(看代碼)

  • 利用 Proxy 代理進行簡單數據校驗
  • 校驗邏輯直接加在代理處理函數中過於繁重,咱們能夠把校驗模塊直接抽離出來,只須要去處理校驗的邏輯,代理層面後續不須要改動

三:利用proxy進行記錄對象訪問

  • 針對那些重度依賴資源,執行緩慢或是頻繁使用的方法或接口,統計它們的使用或是性能es6

  • 能夠記錄各類各樣的信息而不用修改應用程序的代碼或是阻塞代碼執行。而且只須要在這些代碼的基礎上稍事修改就能夠記錄特性函數的執行性能
    github

  • 以上例子就是一個監聽函數執行的代理,能夠將其進行擴展爲打點函數面試

  • 這裏面 Proxytrap 爲何使用 get 而不是 apply ? 答案編程

四:普通函數與構造函數的兼容

  • 構造函數調用沒有使用new關鍵字來調用的話,Class對象會直接拋出異常
  • 使用 Proxy 進行封裝讓構造函數也可以直接進行函數調用

五:深層取值判斷(看代碼)

  • 須要解決的幾個問題

    1. 獲取數據進行攔截
    2. xxx.xxx.xxx...不管 undefined 出如今哪裏都不能報錯
    3. Proxyget() 傳入的參數必須是對象
  • 傳統方式深層取值繁瑣,利用Proxy能夠簡化沒必要要代碼

  • 可是當 target[prop]undefined 的時候,Proxy get()的入參變成了 undefined,但 Proxy 第一個入參必須爲對象

  • 須要對 obj 爲 undefined 的時候進行特殊處理,爲了可以深層取值,因此使用一個空函數進行設置攔截,利用 apply trap 進行處理

  • 咱們理想中的應該是,若是屬性爲 undefined 就返回 undefined,但仍要支持訪問下級屬性,而不是拋出錯誤

  • 順着這個思路來的話,很明顯當屬性爲 undefined 的時候也須要用 Proxy 進行特殊處理
    因此咱們須要一個具備下面特性的 get() 方法

getData(undefined)() === undefined; // true
	getData(undefined).xxx.yyy.zzz(); // undefined
  • 這裏徹底不須要注意 get(undefined).xxx 是否爲正確的值,由於想獲取值必需要執行才能拿到
  • 那麼只須要對全部 undefined 後面訪問的屬性都默認爲 undefined 就行了,因此咱們須要一個代理了 undefined 後的返回對象
  • 同時爲了解決無限循環執行的問題,當第一次檢測到出現 undefined 的時候,中止執行

六:日誌上報

Vue 3.0 的 Proxy & Object.defineProperty

Proxy

  • 劫持方式:代理整個對象,只需作一層代理就能夠監聽同級結構下的全部屬性變化,包括新增屬性和刪除屬性
  • 本質Proxy 本質上屬於元編程非破壞性數據劫持,在原對象的基礎上進行了功能的衍生而又不影響原對象,符合鬆耦合高內聚的設計理念

Object.defineProperty

  • 劫持方式:只能劫持對象的屬性,不能直接代理對象
  • 流程:get中進行依賴收集,set數據時通知訂閱者更新
  • 存在的問題:雖然 Object.defineProperty 經過爲屬性設置 getter/setter 可以完成數據的響應式,可是它並不算是實現數據的響應式的完美方案,某些狀況下須要對其進行修補或者hack,這也是它的缺陷,主要表如今兩個方面:
    • 沒法檢測到對象屬性的新增或刪除
    • 不能監聽數組的變化

1. Object.defineProperty 沒法監聽新增長的屬性

  • 解決方式:提供方法從新手動Observe,須要監聽的話使用 Vue.set() 從新設置添加屬性的響應式

2. Object.defineProperty 沒法一次性監聽對象全部屬性,如對象屬性的子屬性

  • 解決方式: 經過遞歸調用來實現子屬性響應式

3. Object.defineProperty 沒法響應數組操做

  • 解決方式:經過遍歷和重寫Array數組原型方法操做方法實現,可是也只限制在 push/pop/shift/unshift/splice/sort/reverse 這七個方法,其餘數組方法及數組的使用則沒法檢測到,也沒法監聽數組索引的變化和長度的變動

4. Proxy 攔截方式更多, Object.defineProperty 只有 get 和 set

5. Proxy 性能問題

6. Proxy 兼容性差

  • Vue 3.0 中放棄了對於IE的支持(覺得 Vue 3.0 中會對不兼容的瀏覽器進行向下兼容,可是通過查看資料和源碼發現尤大壓根沒作兼容)
  • 目前並無一個完整支持 Proxy 全部攔截方法的 Polyfill 方案,有一個 google 編寫的 proxy-polyfill 也只支持了 get/set/apply/construct 四種攔截

多說一嘴 Decorator

  • ES7 中實現的 Decorator,至關於設計模式中的裝飾器模式。
  • 若是簡單地區分 ProxyDecorator 的使用場景,能夠歸納爲:Proxy 的核心做用是控制外界對被代理者內部的訪問,Decorator 的核心做用是加強被裝飾者的功能。

寫在最後

  • 若是你以爲這篇文章對你有益,煩請點贊以及分享給更多須要的人!

快到碗裏來!百度校招還有HC!甩簡從來!

極速直接內推【字節跳動】&【百度】&【猿輔導】&【京東】

歡迎關注微信公衆號【全棧道路】,獲取更多科技相關知識及免費書籍。

更多好文

幾行代碼教你解決微信生成海報及二維碼

冷門的HTML - tabindex 的做用

[萬字長文]百度和好將來面試經含答案

[前端面試]前端緩存問題看這篇,讓面試官愛上你

記一次慘痛的Vue-cli + VueX + SSR經歷

[三分鐘小文]前端性能優化-HTML、CSS、JS部分

[三分鐘小文]前端性能優化-頁面加載速度優化

[三分鐘小文]前端性能優化-網絡傳輸層優化

相關文章
相關標籤/搜索