欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

茶余飯后聊聊Vue3.0響應(yīng)式數(shù)據(jù)那些事兒

 更新時間:2019年10月30日 09:20:16   作者:政采云前端團隊  
這篇文章主要介紹了茶余飯后聊聊Vue3.0響應(yīng)式數(shù)據(jù)那些事兒,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

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

  1. 使用 Typescript
  2. 放棄 class 采用 function-based API
  3. 重構(gòu) complier
  4. 重構(gòu) virtual DOM
  5. 新的響應(yīng)式機制

今天咱就聊聊重構(gòu)后的響應(yīng)式數(shù)據(jù)。

嘗鮮

重構(gòu)后的 Vue3.0 和之前在寫法上有很大的差別,早前在網(wǎng)絡(luò)上對于 Vue3.0 這種激進式的重構(gòu)方式發(fā)起了一場討論,見仁見智。不多說先看看 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,這種寫法也沒啥特別的。關(guān)鍵是這種響應(yīng)式數(shù)據(jù)的寫法好像在哪里見過有沒有?寫過 React 項目的人可能一眼就能看出來,沒錯就是它 mobx,一種 React 的響應(yīng)式狀態(tài)管理插件

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 暴露的這幾個和響應(yīng)式數(shù)據(jù)相關(guān)的方法:

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

effect(fn)
effect 意思是副作用,此方法默認會先執(zhí)行一次。如果 fn 中有依賴的可觀察屬性變化時,會再次觸發(fā)此回調(diào)函數(shù)

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

mobx 有異曲同工之妙。

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

重構(gòu)后的響應(yīng)式機制帶來了哪些改變?

每一個大版本的發(fā)布都意味著新功能、新特性的出現(xiàn),那么重構(gòu)后的響應(yīng)式數(shù)據(jù)部分相比 3.0 之前的版本有了哪些方面的改變呢?下面聽我娓娓道來:

對數(shù)組的全面監(jiān)聽

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

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

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

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

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

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

const arr = ["2019","云","棲","音","樂","節(jié)"];
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 是雖然有了新歡,但也沒忘記舊愛,對于在之前版本中數(shù)組的幾種方法的監(jiān)聽還是照樣支持的。

惰性監(jiān)聽

什么是"惰性監(jiān)聽"?

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

惰性監(jiān)聽有什么好處?

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

Vue3.0 以前組件實例在初始化的時候會將 data 整個對象變?yōu)榭捎^察對象,通過遞歸的方式給每個 Key 使用Object.defineProperty 加上 getter 和 settter,如果是數(shù)組就重寫代理數(shù)組對象的七個方法。而在 Vue3.0 中,將可響應(yīng)式對象創(chuàng)建的權(quán)利交給了開發(fā)者,開發(fā)者可以通過暴露的 reactive, compted, effect 方法自定義自己需要響應(yīng)式能力的數(shù)據(jù),實例在初始化時不需要再去遞歸 data 對象了,從而降低了組件實例化的時間。

降低了運行內(nèi)存的使用

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

Map、Set、WeakSet、WeakMap的監(jiān)聽

前面提到 Proxy 可以代理所有的對象,立馬聯(lián)想到了 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 對象賦值、取值和他們內(nèi)部的 this 指向有關(guān)系,但這里的 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")

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

Vue3.0 是如何實現(xiàn)集合類型數(shù)據(jù)監(jiān)聽的?

眼尖的同學看完上面這段代碼會發(fā)現(xiàn)一個問題,集合是沒有 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
}

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

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

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

// 可變數(shù)據(jù)插樁對象,以及一系列相應(yīng)的插樁方法
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)
}
// 迭代器相關(guān)的方法
const iteratorMethods = ['keys', 'values', 'entries', Symbol.iterator]
iteratorMethods.forEach(method => {
 mutableInstrumentations[method] = createIterableMethod(method, false)
 readonlyInstrumentations[method] = createIterableMethod(method, true)
})
// 創(chuàng)建getter的函數(shù)
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,所以作者在在這里進行了"偷梁換柱",這里新創(chuàng)建了一個和集合對象具有相同屬性和方法的普通對象,在集合對象 get 操作時將 target 對象換成新創(chuàng)建的普通對象。這樣,當調(diào)用 get 操作時 Reflect 反射到這個新對象上,當調(diào)用 set 方法時就直接調(diào)用新對象上可以觸發(fā)響應(yīng)的方法,是不是很巧妙?所以多看源碼好處多多,可以多學學人家的騷操作。

IE 怎么辦?

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

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

結(jié)語

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

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • VUE前端導出文件之file-saver插件安裝使用教程

    VUE前端導出文件之file-saver插件安裝使用教程

    這篇文章主要給大家介紹了關(guān)于VUE前端導出文件之file-saver插件安裝使用的相關(guān)資料,file-saver是一個用于保存文件的JavaScript庫,它提供了一種簡單的方式來生成和保存文件,支持各種文件類型,例如文本文件、圖片、PDF等,需要的朋友可以參考下
    2024-05-05
  • Vue中Vue router和axios的封裝使用教程

    Vue中Vue router和axios的封裝使用教程

    當用戶登錄后,后臺會返回一個token給前端,前端下次進入首頁后,會先判斷token是否過期,如果過期自動進入登錄頁面,本文給大家介紹Vue中Vue router和axios的封裝使用教程,感興趣的朋友一起看看吧
    2023-11-11
  • 使用vue-antDesign menu頁面方式(添加面包屑跳轉(zhuǎn))

    使用vue-antDesign menu頁面方式(添加面包屑跳轉(zhuǎn))

    這篇文章主要介紹了使用vue-antDesign menu頁面方式(添加面包屑跳轉(zhuǎn)),具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • vue中組件傳值的常見方式小結(jié)

    vue中組件傳值的常見方式小結(jié)

    在 Vue.js 中,組件之間的數(shù)據(jù)傳遞是一個常見的需求,Vue 提供了多種方法來實現(xiàn)這一目標,包括 props、全局事件總線、消息的訂閱與發(fā)布等,下面我們就來學習一下這些方法的具體實現(xiàn)吧
    2023-12-12
  • vue引入css文件導致全局污染的問題

    vue引入css文件導致全局污染的問題

    這篇文章主要介紹了vue引入css文件導致全局污染的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • vue實現(xiàn)輸入框的模糊查詢的示例代碼(節(jié)流函數(shù)的應(yīng)用場景)

    vue實現(xiàn)輸入框的模糊查詢的示例代碼(節(jié)流函數(shù)的應(yīng)用場景)

    這篇文章主要介紹了vue實現(xiàn)輸入框的模糊查詢的示例代碼(節(jié)流函數(shù)的應(yīng)用場景),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-09-09
  • 詳解給Vue2路由導航鉤子和axios攔截器做個封裝

    詳解給Vue2路由導航鉤子和axios攔截器做個封裝

    本篇文章主要介紹了詳解給Vue2路由導航鉤子和axios攔截器做個封裝,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04
  • Vue 構(gòu)造選項 - 進階使用說明

    Vue 構(gòu)造選項 - 進階使用說明

    這篇文章主要介紹了Vue 構(gòu)造選項 - 進階使用說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • vue2項目中全局封裝axios問題

    vue2項目中全局封裝axios問題

    這篇文章主要介紹了vue2項目中全局封裝axios問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • vue?鼠標移入移出(hover)切換顯示圖片問題

    vue?鼠標移入移出(hover)切換顯示圖片問題

    這篇文章主要介紹了vue?鼠標移入移出(hover)切換顯示圖片問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10

最新評論