偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

圖解 React 的 Diff 算法:核心就兩個字 — 復用

開發(fā) 前端
React 是基于 Vdom 的前端框架,組件渲染產(chǎn)生 Vdom,渲染器把 Vdom 渲染成 Dom。

React 是基于 vdom 的前端框架,組件 render 產(chǎn)生 vdom,然后渲染器把 vdom 渲染出來。

state 更新的時候,組件會重新 render,產(chǎn)生新的 vdom,在瀏覽器平臺下,為了減少 dom 的創(chuàng)建,React 會對兩次的 render 結果做 diff,盡量復用 dom,提高性能。

diff 算法是前端框架中比較復雜的部分,代碼比較多,但今天我們不上代碼,只看圖來理解它。

首先,我們先過一下 react 的 fiber 架構:

Fiber 架構

React 是通過 jsx 描述頁面結構的:

function Profile() {
return <div>
<img src="avatar.png" className="profile" />
<h3>{[user.firstName, user.lastName].join(" ")}</h3>
</div>
}

經(jīng)過 babel 等的編譯會變成 render function:

import { jsx as _jsx } from "react/jsx-runtime";
import { jsxs as _jsxs } from "react/jsx-runtime";
const profile = _jsxs("div", {
children: [
_jsx("img", {
src: "avatar.png",
className: "profile",
}),
_jsx("h3", {
children: [user.firstName, user.lastName].join(" "),
}),
],
});

render function 執(zhí)行結果就是 vdom,也就是 React Element 的實例:

圖片圖片

在 16 之前,React 是直接遞歸渲染 vdom 的,setState 會觸發(fā)重新渲染,對比渲染出的新舊 vdom,對差異部分進行 dom 操作。

在 16 之后,為了優(yōu)化性能,會先把 vdom 轉(zhuǎn)換成 fiber,也就是從樹轉(zhuǎn)換成鏈表,然后再渲染。整體渲染流程分成了兩個階段:

  • render 階段:從 vdom 轉(zhuǎn)換成 fiber,并且對需要 dom 操作的節(jié)點打上 effectTag 的標記。
  • commit 階段:對有 effectTag 標記的 fiber 節(jié)點進行 dom 操作,并執(zhí)行所有的 effect 副作用函數(shù)。

從 vdom 轉(zhuǎn)成 fiber 的過程叫做 reconcile(調(diào)和),這個過程是可以打斷的,由 scheduler 調(diào)度執(zhí)行。

圖片圖片

diff 算法作用在 reconcile 階段:

第一次渲染不需要 diff,直接 vdom 轉(zhuǎn) fiber。

再次渲染的時候,會產(chǎn)生新的 vdom,這時候要和之前的 fiber 做下對比,決定怎么產(chǎn)生新的 fiber,對可復用的節(jié)點打上修改的標記,剩余的舊節(jié)點打上刪除標記,新節(jié)點打上新增標記。

接下來我們就來詳細了解下 React 的 diff 算法:

React 的 diff 算法

在講 diff 算法實現(xiàn)之前,我們要先想明白為什么要做 diff,不做行么?

當然可以,每一次渲染都直接把 vdom 轉(zhuǎn)成 fiber 就行,不用和之前的做對比,這樣是可行的。

其實 SSR 的時候就不用做 diff,因為會把組件渲染成字符串,第二次渲染也是產(chǎn)生字符串,難道這時候還要和之前的字符串對比下,有哪些字符串可以復用么?

不需要,SSR 的時候就沒有 diff,每次都是 vdom 渲染出新的字符串。

那為什么瀏覽器里要做 diff 呢?

因為 dom 創(chuàng)建的性能成本很高,如果不做 dom 的復用,那前端框架的性能就太差了。

diff 算法的目的就是對比兩次渲染結果,找到可復用的部分,然后剩下的該刪除刪除,該新增新增。

那具體怎么實現(xiàn) React 的 diff 算法呢?

比如父節(jié)點下有 A、B、C、D 四個子節(jié)點,那渲染出的 vdom 就是這樣的:

圖片

經(jīng)過 reconcile 之后,會變成這樣的 fiber 結構:

圖片

那如果再次渲染的時候,渲染出了 A、C、B、E 的 vdom,這時候怎么處理呢?

圖片

再次渲染出 vdom 的時候,也要進行 vdom 轉(zhuǎn) fiber 的 reconcile 階段,但是要盡量能復用之前的節(jié)點。

那怎么復用呢?

一一對比下不就行了?

先把之前的 fiber 節(jié)點放到一個 map 里,key 就是節(jié)點的 key:

圖片

然后每個新的 vdom 都去這個 map 里查找下有沒有可以復用的,找到了的話就移動過來,打上更新的 effectTag:

圖片

這樣遍歷完 vdom 節(jié)點之后,map 里剩下一些,這些是不可復用的,那就刪掉,打上刪除的 effectTag;如果 vdom 中還有一些沒找到復用節(jié)點的,就直接創(chuàng)建,打上新增的 effectTag。

這樣就實現(xiàn)了更新時的 reconcile,也就是上面的 diff 算法。其實核心就是找到可復用的節(jié)點,剩下的舊節(jié)點刪掉,新節(jié)點新增。

但有的時候可以再簡化一下,比如上次渲染是 A、B、C、D,這次渲染也是 A、B、C、D,那直接順序?qū)Ρ认戮托?,沒必要建立 map 再找。

所以 React 的 diff 算法是分成兩次遍歷的:

第一輪遍歷,一一對比 vdom 和老的 fiber,如果可以復用就處理下一個節(jié)點,否則就結束遍歷。

如果所有的新的 vdom 處理完了,那就把剩下的老 fiber 節(jié)點刪掉就行。

如果還有 vdom 沒處理,那就進行第二次遍歷:

第二輪遍歷,把剩下的老 fiber 放到 map 里,遍歷剩下的 vdom,從 map 里查找,如果找到了,就移動過來。

第二輪遍歷完了之后,把剩余的老 fiber 刪掉,剩余的 vdom 新增。

這樣就完成了新的 fiber 結構的創(chuàng)建,也就是 reconcile 的過程。

比如上面那個例子,第一輪遍歷就是這樣的:

圖片

一一對比新的 vdom 和 老的 fiber,發(fā)現(xiàn) A 是可以復用的,那就創(chuàng)建新 fiber 節(jié)點,打上更新標記。

C 不可復用,所以結束第一輪遍歷,進入第二輪遍歷。

圖片

把剩下的 老 fiber 節(jié)點放到 map 里,然后遍歷新的 vdom 節(jié)點,從 map 中能找到的話,就是可復用,移動過來打上更新的標記。

遍歷完之后,剩下的老 fiber 節(jié)點刪掉,剩下的新 vdom 新增。

這樣就完成了更新時的 reconcile 的過程。

總結

react 是基于 vdom 的前端框架,組件渲染產(chǎn)生 vdom,渲染器把 vdom 渲染成 dom。

瀏覽器下使用 react-dom 的渲染器,會先把 vdom 轉(zhuǎn)成 fiber,找到需要更新 dom 的部分,打上增刪改的 effectTag 標記,這個過程叫做 reconcile,可以打斷,由 scheducler 調(diào)度執(zhí)行。reconcile 結束之后一次性根據(jù) effectTag 更新 dom,叫做 commit。

這就是 react 的基于 fiber 的渲染流程,分成 render(reconcile + schedule)、commit 兩個階段。

當渲染完一次,產(chǎn)生了 fiber 之后,再次渲染的 vdom 要和之前的 fiber 對比下,再決定如何產(chǎn)生新的 fiber,目標是盡可能復用已有的 fiber 節(jié)點,這叫做 diff 算法。

react 的 diff 算法分為兩個階段:

第一個階段一一對比,如果可以復用就下一個,不可以復用就結束。

第二個階段把剩下的老 fiber 放到 map 里,遍歷剩余的 vdom,一一查找 map 中是否有可復用的節(jié)點。

最后把剩下的老 fiber 刪掉,剩下的新 vdom 新增。

這樣就完成了更新時的 reconcile 過程。

其實 diff 算法的核心就是復用節(jié)點,通過一一對比也好,通過 map 查找也好,都是為了找到可復用的節(jié)點,移動過來。然后剩下的該刪刪該增增。

理解了如何找到可復用的節(jié)點,就理解了 diff 算法的核心。

責任編輯:姜華 來源: 神光的編程秘籍
相關推薦

2022-04-15 08:07:21

ReactDiff算法

2022-07-09 20:35:23

數(shù)字化企業(yè)轉(zhuǎn)型

2021-02-01 08:33:16

Redis排序內(nèi)存

2024-12-05 09:45:25

Reactdiff 算法前端開發(fā)

2022-04-01 11:39:32

互聯(lián)網(wǎng)裁員紅利

2021-05-13 07:30:27

Kafka消息流系統(tǒng)

2023-07-03 07:51:47

2022-05-06 07:19:11

DOMDiff算法

2017-06-14 13:42:00

字典數(shù)據(jù)社交

2011-09-01 21:41:42

SQL Server把字符分割成兩個字符串

2020-10-26 08:19:53

算法隊列

2023-03-14 07:23:48

ReactJSX語法

2020-05-20 14:25:45

Reactreact.js前端

2022-06-28 15:13:12

Vuediff 算法

2023-12-06 07:16:31

Go語言語句

2021-08-03 08:13:47

數(shù)據(jù)

2022-01-05 09:40:03

DIff算法前端

2017-06-05 11:23:45

LinuxDiff和Meld工具

2010-03-04 09:50:14

企業(yè)定制軟件開發(fā)

2022-12-07 11:21:30

Reactdiff
點贊
收藏

51CTO技術棧公眾號