成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

剖析 React 源碼:render 流程(二)

My_Oh_My / 2635人閱讀

摘要:就是,如果你不了解這個的話可以閱讀下相關文檔,是應用初始化時就會生成的一個變量,值也是,并且這個值不會在后期再被改變。

這是我的剖析 React 源碼的第三篇文章,如果你沒有閱讀過之前的文章,請務必先閱讀一下 第一篇文章 中提到的一些注意事項,能幫助你更好地閱讀源碼。

文章相關資料

React 16.8.6 源碼中文注釋,這個鏈接是文章的核心,文中的具體代碼及代碼行數(shù)都是依托于這個倉庫

熱身篇

render 流程(一)

此篇文章內(nèi)容銜接 render 流程(一),當然不看上一篇文章也沒什么問題,因為內(nèi)容并沒有強相關。

現(xiàn)在請大家打開 我的代碼 并定位到 react-dom 文件夾下的 src 中的 ReactDOM.js 文件,今天的內(nèi)容會從這里開始。

ReactRoot.prototype.render

在上一篇文章中,我們介紹了當 ReactDom.render 執(zhí)行時,內(nèi)部會首先判斷是否已經(jīng)存在 root,沒有的話會去創(chuàng)建一個 root。在今天的文章中,我們將會了解到存在 root 以后會發(fā)生什么事情。

大家可以先定位到代碼的第 592 行。

大家可以看到,在上述的代碼中調(diào)用了 unbatchedUpdates 函數(shù),這個函數(shù)涉及到的知識其實在 React 中相當重要。

大家都知道多個 setState 一起執(zhí)行,并不會觸發(fā) React 的多次渲染。

// 雖然 age 會變成 3,但不會觸發(fā) 3 次渲染
this.setState({ age: 1 })
this.setState({ age: 2 })
this.setState({ age: 3 })

這是因為內(nèi)部會將這個三次 setState 優(yōu)化為一次更新,術語是批量更新(batchedUpdate),我們在后續(xù)的內(nèi)容中也能看到內(nèi)部是如何處理批量更新的。

對于 root 來說其實沒必要去批量更新,所以這里調(diào)用了 unbatchedUpdates 函數(shù)來告知內(nèi)部不需要批量更新。

然后在 unbatchedUpdates 回調(diào)內(nèi)部判斷是否存在 parentComponent。這一步我們可以假定不會存在 parentComponent,因為很少有人會在 root 外部加上 context 組件。不存在 parentComponent 的話就會執(zhí)行 root.render(children, callback),這里的 render 指的是 ReactRoot.prototype.render。

render 函數(shù)內(nèi)部我們首先取出 root,這里的 root 指的是 FiberRoot,如果你想了解 FiberRoot 相關的內(nèi)容可以閱讀 上一篇文章。然后創(chuàng)建了 ReactWork 的實例,這塊內(nèi)容我們沒有必要深究,功能就是為了在組件渲染或更新后把所有傳入 ReactDom.render 中的回調(diào)函數(shù)全部執(zhí)行一遍。

接下來我們來看 updateContainer 內(nèi)部是怎么樣的。

我們先從 FiberRoot 的 current 屬性中取出它的 fiber 對象,然后計算了兩個時間。這兩個時間在 React 中相當重要,因此我們需要多帶帶用一小節(jié)去學習它們。

時間

首先是 currentTime,在 requestCurrentTime 函數(shù)內(nèi)部計算時間的最核心函數(shù)是 recomputeCurrentRendererTime

function recomputeCurrentRendererTime() {
  const currentTimeMs = now() - originalStartTimeMs;
  currentRendererTime = msToExpirationTime(currentTimeMs);
}

now() 就是 performance.now(),如果你不了解這個 API 的話可以閱讀下 相關文檔,originalStartTimeMs 是 React 應用初始化時就會生成的一個變量,值也是 performance.now(),并且這個值不會在后期再被改變。那么這兩個值相減以后,得到的結果也就是現(xiàn)在離 React 應用初始化時經(jīng)過了多少時間。

然后我們需要把計算出來的值再通過一個公式算一遍,這里的 | 0 作用是取整數(shù),也就是說 11 / 10 | 0 = 1

接下來我們來假定一些變量值,代入公式來算的話會更方便大家理解。

假如 originalStartTimeMs2500,當前時間為 5000,那么算出來的差值就是 2500,也就是說當前距離 React 應用初始化已經(jīng)過去了 2500 毫秒,最后通過公式得出的結果為:

currentTime = 1073741822 - ((2500 / 10) | 0) = 1073741572

接下來是計算 expirationTime,這個時間和優(yōu)先級有關,值越大,優(yōu)先級越高。并且同步是優(yōu)先級最高的,它的值為 1073741823,也就是之前我們看到的常量 MAGIC_NUMBER_OFFSET 加一。

computeExpirationForFiber 函數(shù)中存在很多分支,但是計算的核心就只有三行代碼,分別是:

// 同步
expirationTime = Sync
// 交互事件,優(yōu)先級較高
expirationTime = computeInteractiveExpiration(currentTime)
// 異步,優(yōu)先級較低
expirationTime = computeAsyncExpiration(currentTime)

接下來我們就來分析 computeInteractiveExpiration 函數(shù)內(nèi)部是如何計算時間的,當然 computeAsyncExpiration 計算時間的方式也是相同的,無非更換了兩個變量。

以上這些代碼其實就是公式,我們把具體的值代入就能算出結果了。

time = 1073741822 - ((((1073741822 - 1073741572 + 15) / 10) | 0) + 1) * 10 = 1073741552

另外在 ceiling 函數(shù)中的 1 * bucketSizeMs / UNIT_SIZE 是為了抹平一段時間內(nèi)的時間差,在抹平的時間差內(nèi)不管有多少個任務需要執(zhí)行,他們的過期時間都是同一個,這也算是一個性能優(yōu)化,幫助渲染頁面行為節(jié)流。

最后其實我們這個計算出來的 expirationTime 是可以反推出另外一個時間的:

export function expirationTimeToMs(expirationTime: ExpirationTime): number {
  return (MAGIC_NUMBER_OFFSET - expirationTime) * UNIT_SIZE;
}

如果我們將之前計算出來的 expirationTime 代入以上代碼,得出的結果如下:

(1073741822 - 1073741552) * 10 = 2700

這個時間其實和我們之前在上文中計算出來的 2500 毫秒差值很接近。因為 expirationTime 指的就是一個任務的過期時間,React 根據(jù)任務的優(yōu)先級和當前時間來計算出一個任務的執(zhí)行截止時間。只要這個值比當前時間大就可以一直讓 React 延后這個任務的執(zhí)行,以便讓更高優(yōu)先級的任務執(zhí)行,但是一旦過了任務的截止時間,就必須讓這個任務馬上執(zhí)行。

這部分的內(nèi)容一直在算來算去,看起來可能有點頭疼。當然如果你嫌麻煩,只需要記住任務的過期時間是通過當前時間加上一個常量(任務優(yōu)先級不同常量不同)計算出來的。

另外其實你還可以在后面的代碼中看到更加直觀且簡單的計算過期時間的方式,但是目前那部分代碼還沒有被使用起來。

scheduleRootUpdate

當我們計算出時間以后就會調(diào)用 updateContainerAtExpirationTime,這個函數(shù)其實沒有什么好解析的,我們直接進入 scheduleRootUpdate 函數(shù)就好。

首先我們會創(chuàng)建一個 update,這個對象和 setState 息息相關

// update 對象的內(nèi)部屬性
expirationTime: expirationTime,
tag: UpdateState,
// setState 的第一二個參數(shù)
payload: null,
callback: null,
// 用于在隊列中找到下一個節(jié)點
next: null,
nextEffect: null,

對于 update 對象內(nèi)部的屬性來說,我們需要重點關注的是 next 屬性。因為 update 其實就是一個隊列中的節(jié)點,這個屬性可以用于幫助我們尋找下一個 update。對于批量更新來說,我們可能會創(chuàng)建多個 update,因此我們需要將這些 update 串聯(lián)并存儲起來,在必要的時候拿出來用于更新 state。

render 的過程中其實也是一次更新的操作,但是我們并沒有 setState,因此就把 payload 賦值為 {element} 了。

接下來我們將 callback 賦值給 update 的屬性,這里的 callback 還是 ReactDom.render 的第二個參數(shù)。

然后我們將剛才創(chuàng)建出來的 update 對象插入隊列中,enqueueUpdate 函數(shù)內(nèi)部分支較多且代碼簡單,這里就不再貼出代碼了,有興趣的可以自行閱讀。函數(shù)核心作用就是創(chuàng)建或者獲取一個隊列,然后把 update 對象入隊。

最后調(diào)用 scheduleWork 函數(shù),這里開始就是調(diào)度相關的內(nèi)容,這部分內(nèi)容我們將在下一篇文章中來詳細解析。

總結

以上就是本文的全部內(nèi)容了,這篇文章其實核心還是放在了計算時間上,因為這個時間和后面的調(diào)度息息相關,最后通過一張流程圖總結一下 render 流程兩篇文章的內(nèi)容。

最后

閱讀源碼是一個很枯燥的過程,但是收益也是巨大的。如果你在閱讀的過程中有任何的問題,都歡迎你在評論區(qū)與我交流。

另外寫這系列是個很耗時的工程,需要維護代碼注釋,還得把文章寫得盡量讓讀者看懂,最后還得配上畫圖,如果你覺得文章看著還行,就請不要吝嗇你的點贊。

下一篇文章還是 render 流程相關的內(nèi)容。

最后,覺得內(nèi)容有幫助可以關注下我的公眾號 「前端真好玩」咯,會有很多好東西等著你。

文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/6881.html

相關文章

  • 剖析 React 源碼render 流程(一)

    摘要:大家可以看到是構造函數(shù)構造出來的,并且內(nèi)部有一個對象,這個對象是本文接下來要重點介紹的對象,接下來我們就來一窺究竟吧。在構造函數(shù)內(nèi)部就進行了一步操作,那就是創(chuàng)建了一個對象,并掛載到了上。下一篇文章還是流程相關的內(nèi)容。這是我的剖析 React 源碼的第二篇文章,如果你沒有閱讀過之前的文章,請務必先閱讀一下 第一篇文章 中提到的一些注意事項,能幫助你更好地閱讀源碼。 文章相關資料 React ...

    hiYoHoo 評論0 收藏0
  • react解析: render的FiberRoot(三)

    摘要:查看創(chuàng)建核心函數(shù)源碼行調(diào)用函數(shù)創(chuàng)建是相關,不用管源碼行這個指的是調(diào)用創(chuàng)建,下面我們將會說到對象源碼行源碼行函數(shù)中,首先創(chuàng)建了一個,然后又創(chuàng)建了一個,它們兩者還是相互引用。 感謝 yck: 剖析 React 源碼解析,本篇文章是在讀完他的文章的基礎上,將他的文章進行拆解和加工,加入我自己的一下理解和例子,便于大家理解。覺得yck寫的真的很棒 。React 版本為 16.8.6,關于源碼的...

    muddyway 評論0 收藏0
  • 剖析 React 源碼:先熱個身

    摘要:我們先來看下這個函數(shù)的一些神奇用法對于上述代碼,也就是函數(shù)來說返回值是。不管你第二個參數(shù)的函數(shù)返回值是幾維嵌套數(shù)組,函數(shù)都能幫你攤平到一維數(shù)組,并且每次遍歷后返回的數(shù)組中的元素個數(shù)代表了同一個節(jié)點需要復制幾次。這是我的 React 源碼解讀課的第一篇文章,首先來說說為啥要寫這個系列文章: 現(xiàn)在工作中基本都用 React 了,由此想了解下內(nèi)部原理 市面上 Vue 的源碼解讀數(shù)不勝數(shù),但是反觀...

    sean 評論0 收藏0
  • 源碼全面剖析 React 組件更新機制

    摘要:把組件看作狀態(tài)機有限狀態(tài)機使用來控制本地狀態(tài)使用來傳遞狀態(tài)前面我們探討了如何映射狀態(tài)到上初始渲染那么接下來我們談談時如何同步狀態(tài)到上的也就是是如何更新組件的是如何對比出頁面變化最小的部分這篇文章會為你解答這些問題在這之前你已經(jīng)了解了版本內(nèi) React 把組件看作狀態(tài)機(有限狀態(tài)機), 使用state來控制本地狀態(tài), 使用props來傳遞狀態(tài). 前面我們探討了 React 如何映射狀態(tài)...

    ?。琛?/span> 評論0 收藏0
  • React-Redux源碼剖析

    摘要:為了能夠更好的使用這個工具,今天就對它進行一下源碼剖析。它內(nèi)部的關鍵代碼是在不指定的時候等于,這就意味著的源碼剖析到此結束,謝謝觀看當然如果指定了剖析就還得繼續(xù)。好了,源碼剖析到此結束,謝謝觀看 React-Redux是用在連接React和Redux上的。如果你想同時用這兩個框架,那么React-Redux基本就是必須的了。為了能夠更好的使用這個工具,今天就對它進行一下源碼剖析。 Pr...

    Shimmer 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<