您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“React的diff算法怎么應(yīng)用”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“React的diff算法怎么應(yīng)用”吧!
React 是基于 vdom 的前端框架,組件 render 產(chǎn)生 vdom,然后渲染器把 vdom 渲染出來。
state 更新的時候,組件會重新 render,產(chǎn)生新的 vdom,在瀏覽器平臺下,為了減少 dom 的創(chuàng)建,React 會對兩次的 render 結(jié)果做 diff,盡量復(fù)用 dom,提高性能。
diff 算法是前端框架中比較復(fù)雜的部分,代碼比較多,但今天我們不上代碼,只看圖來理解它。
首先,我們先過一下 react 的 fiber 架構(gòu):
React 是通過 jsx 描述頁面結(jié)構(gòu)的:
const profile = { return <div> <img src="avatar.png" className="profile" /> <h4>{[user.firstName, user.lastName].join(" ")}</h4> </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("h4", { children: [user.firstName, user.lastName].join(" "), }), ], });
render function 執(zhí)行結(jié)果就是 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,對可復(fù)用的節(jié)點打上修改的標記,剩余的舊節(jié)點打上刪除標記,新節(jié)點打上新增標記。
接下來我們就來詳細了解下 React 的 diff 算法:
在講 diff 算法實現(xiàn)之前,我們要先想明白為什么要做 diff,不做行么?
當然可以,每一次渲染都直接把 vdom 轉(zhuǎn)成 fiber 就行,不用和之前的做對比,這樣是可行的。
其實 SSR 的時候就不用做 diff,因為會把組件渲染成字符串,第二次渲染也是產(chǎn)生字符串,難道這時候還要和之前的字符串對比下,有哪些字符串可以復(fù)用么?
不需要,SSR 的時候就沒有 diff,每次都是 vdom 渲染出新的字符串。
那為什么瀏覽器里要做 diff 呢?
因為 dom 創(chuàng)建的性能成本很高,如果不做 dom 的復(fù)用,那前端框架的性能就太差了。
diff 算法的目的就是對比兩次渲染結(jié)果,找到可復(fù)用的部分,然后剩下的該刪除刪除,該新增新增。
那具體怎么實現(xiàn) React 的 diff 算法呢?
比如父節(jié)點下有 A、B、C、D 四個子節(jié)點,那渲染出的 vdom 就是這樣的:
經(jīng)過 reconcile 之后,會變成這樣的 fiber 結(jié)構(gòu):
那如果再次渲染的時候,渲染出了 A、C、B、E 的 vdom,這時候怎么處理呢?
再次渲染出 vdom 的時候,也要進行 vdom 轉(zhuǎn) fiber 的 reconcile 階段,但是要盡量能復(fù)用之前的節(jié)點。
那怎么復(fù)用呢?
一一對比下不就行了?
先把之前的 fiber 節(jié)點放到一個 map 里,key 就是節(jié)點的 key:
然后每個新的 vdom 都去這個 map 里查找下有沒有可以復(fù)用的,找到了的話就移動過來,打上更新的 effectTag:
這樣遍歷完 vdom 節(jié)點之后,map 里剩下一些,這些是不可復(fù)用的,那就刪掉,打上刪除的 effectTag;如果 vdom 中還有一些沒找到復(fù)用節(jié)點的,就直接創(chuàng)建,打上新增的 effectTag。
這樣就實現(xiàn)了更新時的 reconcile,也就是上面的 diff 算法。其實核心就是找到可復(fù)用的節(jié)點,剩下的舊節(jié)點刪掉,新節(jié)點新增。
但有的時候可以再簡化一下,比如上次渲染是 A、B、C、D,這次渲染也是 A、B、C、D,那直接順序?qū)Ρ认戮托?,沒必要建立 map 再找。
所以 React 的 diff 算法是分成兩次遍歷的:
第一輪遍歷,一一對比 vdom 和老的 fiber,如果可以復(fù)用就處理下一個節(jié)點,否則就結(jié)束遍歷。
如果所有的新的 vdom 處理完了,那就把剩下的老 fiber 節(jié)點刪掉就行。
如果還有 vdom 沒處理,那就進行第二次遍歷:
第二輪遍歷,把剩下的老 fiber 放到 map 里,遍歷剩下的 vdom,從 map 里查找,如果找到了,就移動過來。
第二輪遍歷完了之后,把剩余的老 fiber 刪掉,剩余的 vdom 新增。
這樣就完成了新的 fiber 結(jié)構(gòu)的創(chuàng)建,也就是 reconcile 的過程。
比如上面那個例子,第一輪遍歷就是這樣的:
一一對比新的 vdom 和 老的 fiber,發(fā)現(xiàn) A 是可以復(fù)用的,那就創(chuàng)建新 fiber 節(jié)點,打上更新標記。
C 不可復(fù)用,所以結(jié)束第一輪遍歷,進入第二輪遍歷。
把剩下的 老 fiber 節(jié)點放到 map 里,然后遍歷新的 vdom 節(jié)點,從 map 中能找到的話,就是可復(fù)用,移動過來打上更新的標記。
遍歷完之后,剩下的老 fiber 節(jié)點刪掉,剩下的新 vdom 新增。
這樣就完成了更新時的 reconcile 的過程。
到此,相信大家對“React的diff算法怎么應(yīng)用”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學習!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。