亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

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

發布時間:2020-10-24 09:13:22 來源:腳本之家 閱讀:122 作者:政采云前端團隊 欄目:web開發

"別再更新了,實在是學不動了"這句話道出了多少前端開發者的心聲,"不幸"的是 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 在寫法上激進到什么程度。

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Document</title>
 <script src="../packages/vue/dist/vue.global.js"></script>
</head>
<body>
 <div id="app"></div>
 <script>
  const { reactive, computed, effect, createApp } = Vue
  const App = {
   template: `
    <div id="box">
      <button @click="add">{{ state.count }}</button>
    </div> 
   `,
   setup() {
    const state = reactive({
     count: 0
    })
    function add() {
     state.count++
    }
    effect(() => {
     console.log('count改變', state.count);
    })
    return {
     state,
     add
    }
   }
  }
  createApp().mount(App, '#app')
 </script>
</body>
</html>

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

import {observable,computed,autorun} from "mobx"
var numbers = observable([1,2,3]);
var sum = computed(() => numbers.reduce((a, b) => a + b, 0));

var disposer = autorun(() => console.log(sum.get()));
// 輸出 '6'
numbers.push(4);
// 輸出 '10'
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 不能監聽數組下標的改變,這可就冤枉人家了,人家也能偵聽數組下標變化的好嗎,不信你看

const arr = ["2019","云","棲","音","樂","節"];
arr.forEach((val,index)=>{
  Object.defineProperty(arr,index,{
    set(newVal){
      console.log("賦值");
    },
    get(){
      console.log("取值");
      return val;
    }
  })
})
let index = arr[1];
//取值
arr[0] = "2050";
//賦值

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

茶余飯后聊聊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 返回一個代理對象,通過代理對象來收集依賴和觸發更新。大概的原理像這段代碼一樣:

const arr = ["2019","云","棲","音","樂","節"];
let ProxyArray = new Proxy(arr,{
  get:function(target, name, value, receiver) {
    console.log("取值")
    return Reflect.get(target,name);
  },
  set: function(target, name, value, receiver) {
    console.log("賦值")
    Reflect.set(target,name, value, receiver);;
  }
 })
 const index = ProxyArray[0];
 //取值
 ProxyArray[0]="2050"
 //賦值

效果和 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 試試水

let map = new Map([["name","zhengcaiyun"]])
let mapProxy = new Proxy(map, {
 get(target, key, receiver) {
  console.log("取值:",key)
  return Reflect.get(target, key, receiver)
 }
})
mapProxy.get("name")

Uncaught TypeError: Method Map.prototype.get called on incompatible receiver [object Object]

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

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

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

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

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

function reactive(target: object) {
 return createReactiveObject(
  target,
  rawToReactive,
  reactiveToRaw,
  mutableHandlers,
  mutableCollectionHandlers
 )
}

function createReactiveObject(
 target: any,
 toProxy: WeakMap<any, any>,
 toRaw: WeakMap<any, any>,
 baseHandlers: ProxyHandler<any>,
 collectionHandlers: ProxyHandler<any>
) {
 //collectionTypes = new Set<Function>([Set, Map, WeakMap, WeakSet])
 const handlers = collectionTypes.has(target.constructor)
  ? collectionHandlers
  : baseHandlers
 //生成代理對象
 observed = new Proxy(target, handlers)
 toProxy.set(target, observed)
 toRaw.set(observed, target)
 if (!targetMap.has(target)) {
  targetMap.set(target, new Map())
 }
 return observed
}

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

export const mutableCollectionHandlers: ProxyHandler<any> = {
 get: createInstrumentationGetter(mutableInstrumentations)
}
export const readonlyCollectionHandlers: ProxyHandler<any> = {
 get: createInstrumentationGetter(readonlyInstrumentations)
}

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

// 可變數據插樁對象,以及一系列相應的插樁方法
const mutableInstrumentations: any = {
 get(key: any) {
  return get(this, key, toReactive)
 },
 get size() {
  return size(this)
 },
 has,
 add,
 set,
 delete: deleteEntry,
 clear,
 forEach: createForEach(false)
}
// 迭代器相關的方法
const iteratorMethods = ['keys', 'values', 'entries', Symbol.iterator]
iteratorMethods.forEach(method => {
 mutableInstrumentations[method] = createIterableMethod(method, false)
 readonlyInstrumentations[method] = createIterableMethod(method, true)
})
// 創建getter的函數
function createInstrumentationGetter(instrumentations: any) {
 return function getInstrumented(
  target: any,
  key: string | symbol,
  receiver: any
 ) {
  target =
   hasOwn(instrumentations, key) && key in target ? instrumentations : target
  return Reflect.get(target, key, receiver)
 }
}

由于 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 正式版本的早日到來。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

柳河县| 老河口市| 砚山县| 栖霞市| 平顶山市| 松阳县| 通化县| 城口县| 盱眙县| 高陵县| 和林格尔县| 什邡市| 蒙城县| 揭阳市| 定日县| 四平市| 余姚市| 天门市| 绵竹市| 贵州省| 祁连县| 霍州市| 方正县| 隆化县| 玛曲县| 卓尼县| 平乐县| 高青县| 武川县| 陇川县| 邵阳县| 寿阳县| 沾益县| 凤台县| 安平县| 汕尾市| 肥西县| 昆明市| 迁西县| 江陵县| 得荣县|