日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

熟悉vue的小伙伴應(yīng)該都知道,談到vue的原理,最重要的莫過(guò)于:響應(yīng)式,虛擬dom及diff算法.

隨著 Vue 3.0 Pre Alpha 版本的公布,我們得以一窺其源碼的實(shí)現(xiàn)。Vue 最巧妙的特性之一是其響應(yīng)式系統(tǒng),而我們也能夠在倉(cāng)庫(kù)的 packages/reactivity 模塊下找到對(duì)應(yīng)的實(shí)現(xiàn)。雖然源碼的代碼量不多,網(wǎng)上的分析文章也有一堆,但是要想清晰地理解響應(yīng)式原理的具體實(shí)現(xiàn)過(guò)程,還是挺費(fèi)腦筋的事情。經(jīng)過(guò)一天的研究和整理,我把其響應(yīng)式系統(tǒng)的原理總結(jié)成了一張圖,而本文也將圍繞這張圖去講述具體的實(shí)現(xiàn)過(guò)程。

 

帶你徹底掌握 Vue 3.0 的響應(yīng)式系統(tǒng)

 

 

一個(gè)基本的例子

Vue 3.0 的響應(yīng)式系統(tǒng)是獨(dú)立的模塊,可以完全脫離 Vue 而使用,所以我們?cè)?clone 了源碼下來(lái)以后,可以直接在 packages/reactivity 模塊下調(diào)試。

  1. 在項(xiàng)目根目錄運(yùn)行 yarn dev reactivity,然后進(jìn)入 packages/reactivity 目錄找到產(chǎn)出的 dist/reactivity.global.js 文件。
  2. 新建一個(gè) index.html,寫(xiě)入如下代碼:
<script src="./dist/reactivity.global.js"></script>
<script>
const { reactive, effect } = VueObserver

const origin = {
  count: 0
}
const state = reactive(origin)

const fn = () => {
  const count = state.count
  console.log(`set count to ${count}`)
}
effect(fn)
</script>

3.在瀏覽器打開(kāi)該文件,于控制臺(tái)執(zhí)行 state.count++,便可看到輸出 set count to 1。

在上述的例子中,我們使用 reactive() 函數(shù)把 origin 對(duì)象轉(zhuǎn)化成了 Proxy 對(duì)象 state;使用 effect() 函數(shù)把 fn() 作為響應(yīng)式回調(diào)。當(dāng) state.count 發(fā)生變化時(shí),便觸發(fā)了 fn()。接下來(lái)我們將以這個(gè)例子結(jié)合上文的流程圖,來(lái)講解這套響應(yīng)式系統(tǒng)是怎么運(yùn)行的。

 

初始化階段

 

帶你徹底掌握 Vue 3.0 的響應(yīng)式系統(tǒng)

 

 

在初始化階段,主要做了兩件事。

  1. 把 origin 對(duì)象轉(zhuǎn)化成響應(yīng)式的 Proxy 對(duì)象 state。
  2. 把函數(shù) fn() 作為一個(gè)響應(yīng)式的 effect 函數(shù)。

首先我們來(lái)分析第一件事。

大家都知道,Vue 3.0 使用了 Proxy 來(lái)代替之前的 Object.defineProperty(),改寫(xiě)了對(duì)象的 getter/setter,完成依賴收集和響應(yīng)觸發(fā)。但是在這一階段中,我們暫時(shí)先不管它是如何改寫(xiě)對(duì)象的 getter/setter 的,這個(gè)在后續(xù)的”依賴收集階段“會(huì)詳細(xì)說(shuō)明。為了簡(jiǎn)單起見(jiàn),我們可以把這部分的內(nèi)容濃縮成一個(gè)只有兩行代碼的 reactive() 函數(shù):

export function reactive(target) {
  const observed = new Proxy(target, handler)
  return observed
}

接下來(lái)我們分析第二件事。

當(dāng)一個(gè)普通的函數(shù) fn() 被 effect() 包裹之后,就會(huì)變成一個(gè)響應(yīng)式的 effect 函數(shù),而 fn() 也會(huì)被立即執(zhí)行一次。

由于在 fn() 里面有引用到 Proxy 對(duì)象的屬性,所以這一步會(huì)觸發(fā)對(duì)象的 getter,從而啟動(dòng)依賴收集。

除此之外,這個(gè) effect 函數(shù)也會(huì)被壓入一個(gè)名為”activeReactiveEffectStack“(此處為 effectStack)的棧中,供后續(xù)依賴收集的時(shí)候使用。

來(lái)看看代碼(完成代碼請(qǐng)看 effect.js):

export function effect (fn) {
  // 構(gòu)造一個(gè) effect
  const effect = function effect(...args) {
    return run(effect, fn, args)
  }
  // 立即執(zhí)行一次
  effect()
  return effect
}

export function run(effect, fn, args) {
  if (effectStack.indexOf(effect) === -1) {
    try {
      // 往池子里放入當(dāng)前 effect
      effectStack.push(effect)
      // 立即執(zhí)行一遍 fn()
      // fn() 執(zhí)行過(guò)程會(huì)完成依賴收集,會(huì)用到 effect
      return fn(...args)
    } finally {
      // 完成依賴收集后從池子中扔掉這個(gè) effect
      effectStack.pop()
    }
  }
}

至此,初始化階段已經(jīng)完成。接下來(lái)就是整個(gè)系統(tǒng)最關(guān)鍵的一步——依賴收集階段。

 

依賴收集階段

 

帶你徹底掌握 Vue 3.0 的響應(yīng)式系統(tǒng)

 

 

這個(gè)階段的觸發(fā)時(shí)機(jī),就是在 effect 被立即執(zhí)行,其內(nèi)部的 fn() 觸發(fā)了 Proxy 對(duì)象的 getter 的時(shí)候。簡(jiǎn)單來(lái)說(shuō),只要執(zhí)行到類似 state.count 的語(yǔ)句,就會(huì)觸發(fā) state 的 getter。

依賴收集階段最重要的目的,就是建立一份”依賴收集表“,也就是圖示的”targetMap"。targetMap 是一個(gè) WeakMap,其 key 值是~~當(dāng)前的 Proxy 對(duì)象 state~~代理前的對(duì)象origin,而 value 則是該對(duì)象所對(duì)應(yīng)的 depsMap。

depsMap 是一個(gè) Map,key 值為觸發(fā) getter 時(shí)的屬性值(此處為 count),而 value 則是觸發(fā)過(guò)該屬性值所對(duì)應(yīng)的各個(gè) effect。

還是有點(diǎn)繞?那么我們?cè)倥e個(gè)例子。假設(shè)有個(gè) Proxy 對(duì)象和 effect 如下:

const state = reactive({
  count: 0,
  age: 18
})

const effect1 = effect(() => {
  console.log('effect1: ' + state.count)
})

const effect2 = effect(() => {
  console.log('effect2: ' + state.age)
})

const effect3 = effect(() => {
  console.log('effect3: ' + state.count, state.age)
})
復(fù)制代碼

那么這里的 targetMap 應(yīng)該為這個(gè)樣子:

帶你徹底掌握 Vue 3.0 的響應(yīng)式系統(tǒng)

 

 

這樣,{ target -> key -> dep } 的對(duì)應(yīng)關(guān)系就建立起來(lái)了,依賴收集也就完成了。代碼如下:

export function track (target, operationType, key) {
  const effect = effectStack[effectStack.length - 1]
  if (effect) {
    let depsMap = targetMap.get(target)
    if (depsMap === void 0) {
      targetMap.set(target, (depsMap = new Map()))
    }

    let dep = depsMap.get(key)
    if (dep === void 0) {
      depsMap.set(key, (dep = new Set()))
    }

    if (!dep.has(effect)) {
      dep.add(effect)
    }
  }
}

弄明白依賴收集表 targetMap 是非常重要的,因?yàn)檫@是整個(gè)響應(yīng)式系統(tǒng)核心中的核心。

 

響應(yīng)階段

回顧上一章節(jié)的例子,我們得到了一個(gè) { count: 0, age: 18 } 的 Proxy,并構(gòu)造了三個(gè) effect。在控制臺(tái)上看看效果:

帶你徹底掌握 Vue 3.0 的響應(yīng)式系統(tǒng)

 

 

效果符合預(yù)期,那么它是怎么實(shí)現(xiàn)的呢?首先來(lái)看看這個(gè)階段的原理圖:

 

帶你徹底掌握 Vue 3.0 的響應(yīng)式系統(tǒng)

 

 

當(dāng)修改對(duì)象的某個(gè)屬性值的時(shí)候,會(huì)觸發(fā)對(duì)應(yīng)的 setter。

setter 里面的 trigger() 函數(shù)會(huì)從依賴收集表里找到當(dāng)前屬性對(duì)應(yīng)的各個(gè) dep,然后把它們推入到 effects 和 computedEffects(計(jì)算屬性) 隊(duì)列中,最后通過(guò) scheduleRun() 挨個(gè)執(zhí)行里面的 effect。

由于已經(jīng)建立了依賴收集表,所以要找到屬性所對(duì)應(yīng)的 dep 也就輕而易舉了,可以看看具體的代碼實(shí)現(xiàn):

export function trigger (target, operationType, key) {
  // 取得對(duì)應(yīng)的 depsMap
  const depsMap = targetMap.get(target)
  if (depsMap === void 0) {
    return
  }
  // 取得對(duì)應(yīng)的各個(gè) dep
  const effects = new Set()
  if (key !== void 0) {
    const dep = depsMap.get(key)
    dep && dep.forEach(effect => {
      effects.add(effect)
    })
  }
  // 簡(jiǎn)化版 scheduleRun,挨個(gè)執(zhí)行 effect
  effects.forEach(effect => {
    effect()
  })
}
復(fù)制代碼

這里的代碼沒(méi)有處理諸如數(shù)組的 length 被修改的一些特殊情況

至此,響應(yīng)式階段完成。

 

總結(jié)

閱讀源碼的過(guò)程充滿了挑戰(zhàn)性,但同時(shí)也常常被 Vue 的一些實(shí)現(xiàn)思路給驚艷到,收獲良多。本文按照響應(yīng)式系統(tǒng)的運(yùn)行過(guò)程,劃分了”初始化“,”依賴收集“和”響應(yīng)式“三個(gè)階段,分別闡述了各個(gè)階段所做的事情,應(yīng)該能夠較好地幫助讀者理解其核心思路。

分享到:
標(biāo)簽:Vue 3.0
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定