新聞中心
基于Vue3.0發(fā)布在GitHub上的第一版源碼(2019.10.05)整理
10年積累的網(wǎng)站制作、網(wǎng)站設(shè)計經(jīng)驗,可以快速應(yīng)對客戶對網(wǎng)站的新想法和需求。提供各種問題對應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識你,你也不認(rèn)識我。但先制作網(wǎng)站后付款的網(wǎng)站建設(shè)流程,更有烏魯木齊免費網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
預(yù)備知識
- ES6 Proxy,整個響應(yīng)式系統(tǒng)的基礎(chǔ)。
- 新的composition-API的基本使用,目前還沒有中文文檔,可以先通過這個倉庫(composition-api-rfc)了解,里面也有對應(yīng)的在線文檔。
先把Vue3.0跑起來
先把vue-next倉庫的代碼clone下來,安裝依賴然后構(gòu)建一下,vue的package下的dist目錄下找到構(gòu)建的腳本,引入腳本即可。
下面一個簡單計數(shù)器的DEMO:
template和之前一樣,同樣Vue3也支持手寫render的寫法,template和render同時存在的情況,優(yōu)先render。
setup選項是新增的主要變動,顧名思義,setup函數(shù)會在組件掛載前(beforeCreate和created生命周期之間)運行一次,類似組件初始化的作用,setup需要返回一個對象或者函數(shù)。返回對象會被賦值給組件實例的renderContext,在組件的模板作用域可以被訪問到,類似data的返回值。返回函數(shù)會被當(dāng)做是組件的render。具體可以細(xì)看文檔。
reactive的作用是將對象包裝成響應(yīng)式對象,通過Proxy代理后的對象。
上面的計數(shù)器的例子,在組件的setup函數(shù)中,創(chuàng)建了一個響應(yīng)式對象state包含一個count屬性。然后創(chuàng)建了一個increment遞增的函數(shù),最后將state和increment返回給作用域,這樣template里的button按鈕就能訪問到increment函數(shù)綁定到點擊的回調(diào),count也能顯示在按鈕上。我們點擊按鈕,按鈕上的數(shù)值就能跟著遞增。
下面切入正題,我們就來探究下按鈕上count值跟著響應(yīng)式更新的原理
數(shù)據(jù)結(jié)構(gòu)
首先列一下主要的一些數(shù)據(jù)結(jié)構(gòu),先列在這里,后面提到可以翻回來看看。
ReactiveEffect 一個Function對象,用于執(zhí)行組件的掛載和更新。
interface ReactiveEffect {
(): any
isEffect: true
active: boolean
raw: Function // 具體執(zhí)行的函數(shù)
deps: Array
computed?: boolean
scheduler?: (run: Function) => void
onTrack?: (event: DebuggerEvent) => void
onTrigger?: (event: DebuggerEvent) => void
onStop?: () => void
}
targetMap 類似 {target -> key -> dep}的一個Map結(jié)構(gòu),用于緩存所有響應(yīng)式對象和依賴收集。
export type Dep = Setexport type KeyToDepMap = Map export const targetMap: WeakMap = new WeakMap()
Proxy代理攔截
reactive函數(shù)執(zhí)行,會將傳入的target對象通過Proxy包裝,攔截它的get,set等,并將代理的target緩存到targetMap,targetMap.set(target, new Map())。
代理的get的時候會調(diào)用一個track函數(shù),而set會調(diào)用一個triger函數(shù)。分別對應(yīng)依賴收集和觸發(fā)更新。
// Proxy get 簡化
function get(target: any, key: string | symbol, receiver: any) {
// 通過key拿到原始值res
const res = Reflect.get(target, key, receiver)
// 過濾不需要代理的情況
// ...
// 依賴收集
track(target, OperationTypes.GET, key)
// 如果取到的值是個對象,將對象再代理包裝一下
// Proxy只能代理對象第一層級
return isObject(res) ? reactive(res) : res
}
// Proxy set 簡化
function set(
target: any,
key: string | symbol,
value: any,
receiver: any
): boolean {
// 一些不需要代理設(shè)置的場景
// ...
// 設(shè)置原始對象的值
const result = Reflect.set(target, key, value, receiver)
// 避免重復(fù)trigger的邏輯
// ...
// 觸發(fā)通知更新
trigger(target, '更新的類型, 新增key或更新key', key)
return result
}
依賴收集和觸發(fā)更新
組件在render階段,視圖會讀取數(shù)據(jù)對象上的值進(jìn)行渲染,此時便觸發(fā)了Proxy的get,由此觸發(fā)對應(yīng)的track函數(shù),記錄下了對應(yīng)的ReactiveEffect,也就是常說的依賴收集。
ReactiveEffect其實就可以看作是組件的更新(mount是特殊的update),數(shù)據(jù)的變更觸發(fā)trigger,trigger遍歷調(diào)用track收集的對應(yīng)的數(shù)據(jù)的ReactiveEffect,也就是對應(yīng)有關(guān)聯(lián)的組件的更新。
trigger觸發(fā)的組件的更新,在render階段又觸發(fā)了新一輪的track依賴收集,更新依賴。
// 簡化的 track
function track(
target: any,
type: OperationTypes,
key?: string | symbol
) {
// 只有在依賴收集階段才進(jìn)行依賴收集
// 除了render,其他場景也可能會觸發(fā)Proxy的get,但不需要進(jìn)行依賴收集
// activeReactiveEffectStack棧頂包裝了當(dāng)前render的組件的mount和update的邏輯
const effect = activeReactiveEffectStack[activeReactiveEffectStack.length - 1]
// 如果effect為空,說明當(dāng)前不在render階段
if (effect) {
// ...
// =====>初始化對應(yīng){target -> key -> dep}的結(jié)構(gòu)
let depsMap = targetMap.get(target)
if (depsMap === void 0) {
targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get(key as string | symbol)
if (!dep) {
depsMap.set(key as string | symbol, (dep = new Set()))
}
// <=====初始化對應(yīng){target -> key -> dep}的結(jié)構(gòu)
// 依賴列表里如果沒有,add
if (!dep.has(effect)) {
// 這里將effect作為依賴,緩存到依賴列表
dep.add(effect)
effect.deps.push(dep)
}
}
}
// 簡化的trigger
function trigger(
target: any,
type: OperationTypes,
key?: string | symbol,
extraInfo?: any
) {
// 獲取對應(yīng)target在track過程中緩存的依賴
const depsMap = targetMap.get(target)
const effects: Set = new Set()
// 省略分類邏輯
depsMap.forEach(dep => {
// 將effect分類過濾添加到effects
})
const run = (effect: ReactiveEffect) => {
// 有個異步調(diào)度的過程,nextTick
scheduleRun(effect, target, type, key, extraInfo)
}
effects.forEach(run)
}
大致流程:

總結(jié)
現(xiàn)在的代碼只有新特性的實現(xiàn),而且ES6+TS的組合可讀性大大提高,編輯器支持也很好,所以相對會好讀很多。這里只是簡單的理了一下vue 3.0 reactive的整體流程,細(xì)節(jié)還有很多地方值得學(xué)習(xí),繼續(xù)加油。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。
本文標(biāo)題:Vue3.0數(shù)據(jù)響應(yīng)式原理詳解
當(dāng)前URL:http://www.dlmjj.cn/article/jsjooc.html


咨詢
建站咨詢
