成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

茶余飯后聊聊 Vue3.0 響應式數據那些事兒

開發 前端
"別再更新了,實在是學不動了"這句話道出了多少前端開發者的心聲,"不幸"的是 Vue 的作者在國慶區間發布了 Vue3.0 的 pre-Aplha 版本,這意味著 Vue3.0 快要和我們見面了。

"別再更新了,實在是學不動了"這句話道出了多少前端開發者的心聲,"不幸"的是 Vue 的作者在國慶區間發布了 Vue3.0 的 pre-Aplha 版本,這意味著 Vue3.0 快要和我們見面了。既來之則安之,扶我起來我要開始講了。Vue3.0 為了達到更快、更小、更易于維護、更貼近原生、對開發者更友好的目的,在很多方面進行了重構:

  1. 使用 Typescript
  2. 放棄 class 采用 function-based API
  3. 重構 complier
  4. 重構 virtual DOM
  5. 新的響應式機制

今天咱就聊聊重構后的響應式數據。

嘗鮮

重構后的 Vue3.0 和之前在寫法上有很大的差別,早前在網絡上對于 Vue3.0 這種激進式的重構方式發起了一場討論,見仁見智。不多說先看看 Vue3.0 在寫法上激進到什么程度。

  1. <!DOCTYPE html> 
  2. <html lang="en"
  3. <head> 
  4.   <meta charset="UTF-8"
  5.   <title>Document</title> 
  6.   <script src="../packages/vue/dist/vue.global.js"></script> 
  7. </head> 
  8. <body> 
  9.   <div id="app"></div> 
  10.   <script> 
  11.     const { reactive, computed, effect, createApp } = Vue 
  12.     const App = { 
  13.       template: ` 
  14.         <div id="box"
  15.             <button @click="add">{{ state.count }}</button> 
  16.         </div>  
  17.       `, 
  18.       setup() { 
  19.         const state = reactive({ 
  20.           count: 0 
  21.         }) 
  22.         function add() { 
  23.           state.count++ 
  24.         } 
  25.         effect(() => { 
  26.           console.log('count改變', state.count); 
  27.         }) 
  28.         return { 
  29.           state, 
  30.           add 
  31.         } 
  32.       } 
  33.     } 
  34.     createApp().mount(App, '#app'
  35.   </script> 
  36. </body> 
  37. </html> 

 

 

 

確實寫法上和 Vue2.x 差別有點大,還整出了個 setup。不過我的第一感覺倒不是寫法上的差異,畢竟寫過 React,這種寫法也沒啥特別的。關鍵是這種響應式數據的寫法好像在哪里見過有沒有?寫過 React 項目的人可能一眼就能看出來,沒錯就是它 mobx ,一種 React 的響應式狀態管理插件

 

  1. import {observable,computed,autorun} from "mobx" 
  2. var numbers = observable([1,2,3]); 
  3. var sum = computed(() => numbers.reduce((a, b) => a + b, 0)); 
  4.  
  5. var disposer = autorun(() => console.log(sum.get())); 
  6. // 輸出 '6' 
  7. numbers.push(4); 
  8. // 輸出 '10' 
  9. numbers.push(5); 

再看看 Vue3.0 暴露的這幾個和響應式數據相關的方法:

  • reactive(value)

創建可觀察的變量,參數可以是 JS 原始類型、引用、純對象、類實例、數組、集合(Map|Set)。

  • effect(fn)

effect 意思是副作用,此方法默認會先執行一次。如果 fn 中有依賴的可觀察屬性變化時,會再次觸發此回調函數

  • computed(()=>expression)

創建一個計算值, computed 實現也是基于 effect 來實現的,特點是 computed 中的函數不會立即執行,多次取值是有緩存機制的, expression 不應該有任何副作用,而僅僅是返回一個值。當這個 expression 依賴的可觀察屬性變化時,這個表達式會重新計算。

和 mobx 有異曲同工之妙。

Vue3.0 把創建響應式對象從組件實例初始化中抽離了出來,通過暴露 API 的方式將響應式對象創建的權利交給開發者,開發者可以自由的決定何時何地創建響應式對象,就沖這點 Vue3.0 我先粉了。

重構后的響應式機制帶來了哪些改變?

每一個大版本的發布都意味著新功能、新特性的出現,那么重構后的響應式數據部分相比 3.0 之前的版本有了哪些方面的改變呢?下面聽我娓娓道來:

對數組的全面監聽

Vue2.x 中被大家吐槽的最多的一點就是針對數組只實現了 push,pop,shift,unshift,splice,sort,reverse' 這七個方法的監聽,以前通過數組下標改變值的時候,是不能觸發視圖更新的。這里插一個題外話,很多人認為 Vue2.x 中數組不能實現全方位監聽是 Object.defineProperty 不能監聽數組下標的改變,這可就冤枉人家了,人家也能偵聽數組下標變化的好嗎,不信你看

 

  1. const arr = ["2019","云","棲","音","樂","節"]; 
  2. arr.forEach((val,index)=>{ 
  3.     Object.defineProperty(arr,index,{ 
  4.         set(newVal){ 
  5.             console.log("賦值"); 
  6.         }, 
  7.         get(){ 
  8.             console.log("取值"); 
  9.             return val; 
  10.         } 
  11.     }) 
  12. }) 
  13. let index = arr[1]; 
  14. //取值 
  15. arr[0] = "2050"
  16. //賦值 

沒毛病,一切變化都在人家的掌握中。上面這段代碼,有沒有人沒看懂,我假裝你們都不懂,貼張圖

茶余飯后聊聊 Vue3.0 響應式數據那些事兒

從數組的數據結構來看,數組也是一個 Key-Value 的鍵值對集合,只是 Key 是數字罷了,自然也可以通過Object.defineProperty 來實現數組的下標訪問和賦值攔截了。其實 Vue2.x 沒有實現數組的全方位監聽主要有兩方面原因:

數組和普通對象相比,JS 數組太"多變"了。比如: arr.length=0 ,可以瞬間清空一個數組; arr[100]=1 又可以瞬間將一個數組的長度變為 100(其他位置用空元素填充),等等騷操作。對于一個普通對象,我們一般只會改變 Key 對應的 Value 值,而不會連key都改變了,而數組就不一樣了 Key 和 Value 都經常增加或減少,因此每次變化后我們都需要重新將整個數組的所有 key 遞歸的使用 Object.defineProperty 加上 setter 和 getter,同時我們還要窮舉每一種數組變化的可能,這樣勢必就會帶來性能開銷問題,有的人會覺得這點性能開銷算個 x 呀,但是性能問題都是由小變大的,如果數組中存的數據量大而且操作頻繁時,這就會是一個大問題。React16.x 在就因為在優化 textNode 的時候,移除了無意義的 span 標簽,性能據說都提升了多少個百分點,所以性能問題不可小看。

數組在應用中經常會被操作,但是通常 push,pop,shift,unshift,splice,sort,reverse 這 7 種操作就能達到目的。因此,出于性能方面的考慮 Vue2.x 做出了一定的取舍。

那么 Vue3.0 怎么又走回頭路去實現了數組的全面監聽了呢?答案就是 Proxy 和 Reflet 這一對原生 CP 的出現,Vue3.0 使用 Proxy 作為響應式數據實現的核心,用 Proxy 返回一個代理對象,通過代理對象來收集依賴和觸發更新。大概的原理像這段代碼一樣:

 

  1. const arr = ["2019","云","棲","音","樂","節"]; 
  2. let ProxyArray = new Proxy(arr,{ 
  3.     get:function(target, name, value, receiver) { 
  4.         console.log("取值"
  5.         return Reflect.get(target,name); 
  6.     }, 
  7.     setfunction(target, name, value, receiver) { 
  8.        console.log("賦值"
  9.        Reflect.set(target,name, value, receiver);; 
  10.     } 
  11.  }) 
  12.  const index = ProxyArray[0]; 
  13.  //取值 
  14.  ProxyArray[0]="2050" 
  15.  //賦值 

效果和 Object.defineProperty 一樣一樣的,又顯得清新脫俗有沒有?而且 Proxy 只要是對象都能代理,后面還會提到。當然 Vue3.0 是雖然有了新歡,但也沒忘記舊愛,對于在之前版本中數組的幾種方法的監聽還是照樣支持的。

惰性監聽

什么是"惰性監聽"?

簡單講就是"偷懶",開發者可以選擇性的生成可觀察對象。在平時的開發中常有這樣的場景,一些頁面上的數據在頁面的整個生命周期中是不會變化的,這時這部分數據不需要具備響應式能力,這在 Vue3.0 以前是沒有選擇余地的,所有在模板中使用到的數據都需要在 data 中定義,組件實例在初始化的時候會將 data 整個對象變為可觀察對象。

惰性監聽有什么好處?

  • 提高了組件實例初始化速度

Vue3.0 以前組件實例在初始化的時候會將 data 整個對象變為可觀察對象,通過遞歸的方式給每個 Key 使用Object.defineProperty 加上 getter 和 settter,如果是數組就重寫代理數組對象的七個方法。而在 Vue3.0 中,將可響應式對象創建的權利交給了開發者,開發者可以通過暴露的 reactive, compted, effect 方法自定義自己需要響應式能力的數據,實例在初始化時不需要再去遞歸 data 對象了,從而降低了組件實例化的時間。

  • 降低了運行內存的使用

Vue3.0 以前生成響應式對象會對對象進行深度遍歷,同時為每個 Key 生成一個 def 對象用來保存 Key 的所有依賴項,當 Key 對應的 Value 變化的時候通知依賴項進行 update。但如果這些依賴項在頁面整個生命周期內不需要更新的時候,這時 def 對象收集的依賴項不僅沒用而且還會占用內存,如果可以在初始化 data 的時候忽略掉這些不會變化的值就好了。Vue3.0 通過暴露的 reactive 方法,開發者可以選擇性的創建可觀察對象,達到減少依賴項的保存,降低了運行內存的使用。

  • Map、Set、WeakSet、WeakMap的監聽

前面提到 Proxy 可以代理所有的對象,立馬聯想到了 ES6 里面新增的集合 Map、Set, 聚合類型的支持得益于 Proxy 和 Reflect。講真的這之前還真不知道 Proxy 這么剛啥都能代理,二話不說直接動手用 Proxy 代理了一個 map 試試水

 

  1. let map = new Map([["name","zhengcaiyun"]]) 
  2. let mapProxy = new Proxy(map, { 
  3.   get(target, key, receiver) { 
  4.     console.log("取值:",key
  5.     return Reflect.get(target, key, receiver) 
  6.   } 
  7. }) 
  8. mapProxy.get("name"
  1. Uncaught TypeError: Method Map.prototype.get called on incompatible receiver [object Object] 

一盆涼水潑來,報錯了。原來 Map、Set 對象賦值、取值和他們內部的 this 指向有關系,但這里的 this 指向的是其實是 Proxy 對象,所以得這樣干

 

  1. let map = new Map([['name','wangyangyang']]) 
  2. let mapProxy = new Proxy(map, { 
  3.   get(target, key, receiver) { 
  4.     var value = Reflect.get(...arguments) 
  5.      console.log("取值:",...arguments) 
  6.     return typeof value == 'function' ? value.bind(target) : value 
  7.   } 
  8. }) 
  9. mapProxy.get("name"

當獲取的是一個函數的時候,通過作用域綁定的方式將原對象綁定到 Map、Set 對象上就好了。

Vue3.0 是如何實現集合類型數據監聽的?

眼尖的同學看完上面這段代碼會發現一個問題,集合是沒有 set 方法,集合賦值用的是 add 操作,那咋辦呢?來看看那么 Vue3.0 是怎么處理的,上一段簡化后的源碼

 

  1. function reactive(target: object) { 
  2.   return createReactiveObject( 
  3.     target, 
  4.     rawToReactive, 
  5.     reactiveToRaw, 
  6.     mutableHandlers, 
  7.     mutableCollectionHandlers 
  8.   ) 
  9.  
  10. function createReactiveObject( 
  11.   target: any
  12.   toProxy: WeakMap<anyany>, 
  13.   toRaw: WeakMap<anyany>, 
  14.   baseHandlers: ProxyHandler<any>, 
  15.   collectionHandlers: ProxyHandler<any
  16. ) { 
  17.   //collectionTypes = new Set<Function>([Set, Map, WeakMap, WeakSet]) 
  18.   const handlers = collectionTypes.has(target.constructor) 
  19.     ? collectionHandlers 
  20.     : baseHandlers 
  21.   //生成代理對象 
  22.   observed = new Proxy(target, handlers) 
  23.   toProxy.set(target, observed) 
  24.   toRaw.set(observed, target) 
  25.   if (!targetMap.has(target)) { 
  26.     targetMap.set(target, new Map()) 
  27.   } 
  28.   return observed 

根據 target 類型適配不同的 handler,如果是集合 ( Map、Set )就使用 collectionHandlers,是其他類型就使用 baseHandlers。接下來看看 collectionHandlers

 

  1. export const mutableCollectionHandlers: ProxyHandler<any> = { 
  2.   get: createInstrumentationGetter(mutableInstrumentations) 
  3. export const readonlyCollectionHandlers: ProxyHandler<any> = { 
  4.   get: createInstrumentationGetter(readonlyInstrumentations) 

沒有意外只有 get,騷就騷在這兒:

 

  1. // 可變數據插樁對象,以及一系列相應的插樁方法 
  2. const mutableInstrumentations: any = { 
  3.   get(keyany) { 
  4.     return get(this, key, toReactive) 
  5.   }, 
  6.   get size() { 
  7.     return size(this) 
  8.   }, 
  9.   has, 
  10.   add
  11.   set
  12.   delete: deleteEntry, 
  13.   clear, 
  14.   forEach: createForEach(false
  15. // 迭代器相關的方法 
  16. const iteratorMethods = ['keys''values''entries', Symbol.iterator] 
  17. iteratorMethods.forEach(method => { 
  18.   mutableInstrumentations[method] = createIterableMethod(method, false
  19.   readonlyInstrumentations[method] = createIterableMethod(method, true
  20. }) 
  21. // 創建getter的函數 
  22. function createInstrumentationGetter(instrumentations: any) { 
  23.   return function getInstrumented( 
  24.     target: any
  25.     key: string | symbol, 
  26.     receiver: any 
  27.   ) { 
  28.     target = 
  29.       hasOwn(instrumentations, key) && key in target ? instrumentations : target 
  30.     return Reflect.get(target, key, receiver) 
  31.   } 

由于 Proxy 的 traps 跟 Map|Set 集合的原生方法不一致,因此無法通過 Proxy 劫持 set,所以作者在在這里進行了"偷梁換柱",這里新創建了一個和集合對象具有相同屬性和方法的普通對象,在集合對象 get 操作時將 target 對象換成新創建的普通對象。這樣,當調用 get 操作時 Reflect 反射到這個新對象上,當調用 set 方法時就直接調用新對象上可以觸發響應的方法,是不是很巧妙?所以多看源碼好處多多,可以多學學人家的騷操作。

IE 怎么辦?

這是個實在不想提但又繞不開的話題,IE 在前端開發者眼里和魔鬼沒什么區別。在 Vue3.0 之前,響應式數據的實現是依賴 ES5 的 Object.defineProperty,因此只要支持 ES5 的瀏覽器都支持 Vue,也就是說 Vue2.x 能支持到 IE9。Vue3.0 依賴的是 Proxy 和 Reflect 這一對出生新時代的 CP,且無法被轉譯成 ES5,或者通過 Polyfill 提供兼容,這就尷尬了。開發者技術前線獲悉的信息,官方在發布最終版本之前會做到兼容 IE11,至于更低版本的 IE 那就只有送上一曲涼涼了。

其實也不用太糾結IE的問題,因為連微軟自己都已經放棄治療 IE 擁抱 Chromium 了,我們又何必糾結呢?

結語

在使用開源框架時不要忘了,我們之所以能免費試用他,靠的維護者投入的大量精力。希望我們多去發現它帶來的優點和作者想通過它傳遞的編程思想。最后期待 Vue3.0 正式版本的早日到來。

責任編輯:未麗燕 來源: ZooTeam Blog
相關推薦

2021-06-02 08:33:31

TPCTPC-H系統

2022-05-23 08:34:08

微前端微服務開發

2020-10-13 08:24:31

Vue3.0系列

2021-05-10 08:58:09

Harbor架構Registry 服務

2013-01-11 16:05:41

求職招聘

2022-04-14 11:50:39

函數組件hook

2022-06-02 08:42:15

Redis數據庫

2021-01-13 11:11:29

TCP連接耗時網絡協議

2021-03-02 11:06:17

工業互聯網

2018-04-24 09:05:09

容器存儲接口

2020-09-17 13:43:03

等保2.0網絡安全漏洞

2020-08-25 09:50:35

Vue3.0命令前端

2022-02-06 22:13:47

VueVue3.0Vue項目

2013-06-13 11:29:14

分布式分布式緩存

2021-04-29 10:30:58

MySQL數據遷移

2020-09-28 15:48:37

開源技術 軟件

2019-12-06 10:44:53

Vue 3.0響應式系統前端

2021-03-09 22:29:46

Vue 響應式API

2018-02-02 13:58:59

數據存儲

2021-04-02 10:30:18

Vue3.0前端代碼
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久久伊人在 | 国产精品自拍视频网站 | 亚洲一区二区三区在线 | 天天干天天操 | 久久国产精品72免费观看 | 久久精品色视频 | 亚洲国产精品激情在线观看 | 久操av在线| 国产精品伦理一区二区三区 | 91福利网| 久久99精品久久久久久国产越南 | 国产91丝袜在线播放 | 成人在线a | 男女羞羞视频在线观看 | 国产一区二区三区在线 | 国产精品国产a级 | 国产精品永久免费观看 | 成年免费视频 | 毛片一级片 | 国产二区三区 | 无码日韩精品一区二区免费 | 欧美精品一区在线 | 视频在线一区二区 | avmans最新导航地址 | 中文字幕视频在线观看 | 免费观看一级特黄欧美大片 | 古装三级在线播放 | 蜜月aⅴ免费一区二区三区 99re在线视频 | 69av片| 一二区成人影院电影网 | 做a的各种视频 | 黄色大片毛片 | 精品乱码一区二区 | 日韩精品成人在线 | 毛片国产 | 成年人在线观看 | 欧美日韩大片 | 亚洲36d大奶网 | 国产精品久久久久久久久大全 | 亚洲午夜精品一区二区三区他趣 | 婷婷不卡 |