網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
VDOM(虛擬dom)
react和vue都是基于vdom的前端框架。
web界面由DOM樹(shù)來(lái)構(gòu)建,當(dāng)其中一部分發(fā)生變化時(shí),其實(shí)就是對(duì)應(yīng)的某個(gè)節(jié)點(diǎn)發(fā)生了變化。
若一次操作中有十次更新DOM的動(dòng)作,虛擬DOM不會(huì)立即操作DOM,而是將這十次更新的diff內(nèi)容保存到本地的一個(gè)js對(duì)象中,最終將這個(gè)js對(duì)象一次性attach到DOM樹(shù)上,再進(jìn)行后續(xù)的操作。
用js對(duì)象模擬DOM節(jié)點(diǎn)的好處是:
- 頁(yè)面的更新可以先全部反映在js對(duì)象(虛擬DOM)上,精準(zhǔn)的對(duì)比關(guān)心的屬性,避免大量無(wú)謂的計(jì)算。等更新完成后,再將最終的js對(duì)象映射成真是的DOM,交由瀏覽器去繪制。
- 為應(yīng)用帶來(lái)的跨平臺(tái)的能力,不再僅僅局限于瀏覽器端。比如:React-Native、canvas等。
//虛擬dom對(duì)象
{
type: 'div',
props: {
id: 'aaa',
className: ['bbb', 'ccc'],
onClick: function() {}
},
children: []
}
react中,jsx由babel轉(zhuǎn)義再經(jīng)過(guò)render后生成我們想要的VDOM(虛擬DOM)。
Fiber架構(gòu)
react15的時(shí)候,和 vue 的渲染流程還是很像的,都是遞歸渲染 vdom,增刪改 dom 就行。
但是因?yàn)闋顟B(tài)管理的差異導(dǎo)致了架構(gòu)的差異。react的setState會(huì)渲染整個(gè)vdom,而一個(gè)應(yīng)用的所有 vdom 可能是很龐大的,計(jì)算量就可能很大。
瀏覽器里 js 計(jì)算時(shí)間太長(zhǎng)是會(huì)阻塞渲染的,會(huì)占用每一幀的動(dòng)畫(huà)、重繪重排的時(shí)間,這樣動(dòng)畫(huà)就會(huì)卡頓。
那能不能把計(jì)算量拆分一下,每一幀計(jì)算一部分,不要阻塞動(dòng)畫(huà)的渲染呢?
順著這個(gè)思路,react 就改造為了 fiber 架構(gòu),目標(biāo)是打斷計(jì)算,分多次進(jìn)行。
渲染的時(shí)候不要直接更新到 dom 了,只找到變化的部分,打個(gè)增刪改的標(biāo)記,創(chuàng)建好 dom,等全部計(jì)算完了一次性更新到 dom 就好了。
初始化渲染
根據(jù) React Element 生成對(duì)應(yīng)的 Fiber 樹(shù)
1.根據(jù) React Element 生成對(duì)應(yīng)的 Fiber 樹(shù)
首次執(zhí)行ReactDOM.render 會(huì)創(chuàng)建fiberRoot 和 rootFiber。
- fiberRoot:整個(gè)應(yīng)用的根節(jié)點(diǎn)(只能有一個(gè))
- rootFiber:組件樹(shù)的根節(jié)點(diǎn)(可以有多個(gè))
2.根據(jù)組件返回的JSX在內(nèi)存中依次創(chuàng)建Fiber節(jié)點(diǎn),并連接在一起構(gòu)建形成Fiber樹(shù),被稱為workInProgress Fiber樹(shù)。最后以 workInProgress 作為最新的渲染樹(shù),fiberRoot 的 current 指針指向 workInProgress 使其變?yōu)?current Fiber 樹(shù)。到此完成初始化流程。
- workInProgress:正在內(nèi)存中構(gòu)建的 Fiber 樹(shù)稱為 workInProgress Fiber樹(shù)。在一次更新中,所有的更新都是發(fā)生在 workInProgress 樹(shù)上。在一次更新之后,workInProgress 樹(shù)上的狀態(tài)是最新的狀態(tài),那么它將變成 current 樹(shù)用于渲染視圖。
- current:正在視圖層渲染的樹(shù)叫做 current 樹(shù)。
更新時(shí)
render階段,創(chuàng)建 dom,打上增刪改的 tag,等全部計(jì)算完之后,commit 階段一次性更新到 dom。
render階段
beginWork和completeWork階段會(huì)循環(huán)最新的jsx生成的虛擬dom,通過(guò)對(duì)比虛擬dom和current Fiber樹(shù)生成workinProgress Fiber樹(shù)。
beginWork
創(chuàng)建本次循環(huán)主體的子Fiber節(jié)點(diǎn)
- mount(首屏渲染)時(shí)創(chuàng)建子Fiber節(jié)點(diǎn),并返回改新建節(jié)點(diǎn);
- update時(shí)若不滿足復(fù)用條件,則與mount時(shí)一樣創(chuàng)建新的子fiber節(jié)點(diǎn),并diff出相應(yīng)的effTag掛在FIber節(jié)點(diǎn)上,并返回該新建節(jié)點(diǎn);
- updata時(shí)若滿足復(fù)用條件,且判斷仍需處理其子節(jié)點(diǎn)的后代,則返回復(fù)用后的子Fiber節(jié)點(diǎn)
- updata時(shí)若滿足復(fù)用條件,且判斷不需繼續(xù)處理其子節(jié)點(diǎn)的后代,則直接返回null值;
completeWork
構(gòu)建或更新DOM節(jié)點(diǎn)
- 構(gòu)建過(guò)程中,會(huì)自下而上將子節(jié)點(diǎn)插入到當(dāng)前節(jié)點(diǎn)
- 更新過(guò)程中,會(huì)計(jì)算DOM 節(jié)點(diǎn)的屬性,一旦屬性需要更新,會(huì)為DOM節(jié)點(diǎn)對(duì)應(yīng)的workINProgress節(jié)點(diǎn)標(biāo)記Update的effectTag
自下而上收集effectList,最終收集到root上
Diff到底是誰(shuí)跟誰(shuí)比?
Diff 是 vdom 和 current Fiber 對(duì)比,生成 workInProgressFiber
effectTag與effectList
render階段不會(huì)真正操作dom,只會(huì)創(chuàng)建dom然后打個(gè)effectTag的增刪改標(biāo)記。commit階段就根據(jù)標(biāo)記來(lái)更新dom就可以了。
但是commit階段要在遍歷一次fiber來(lái)查找有effectTag的節(jié)點(diǎn),更新dom嗎?
當(dāng)然沒(méi)問(wèn)題,但是顯然很低效。完全可以把有 effectTag 的節(jié)點(diǎn)收集到一個(gè)鏈表里,然后 commit 階段直接遍歷這個(gè)鏈表就行了。這個(gè)鏈表叫做 effectList。
react 會(huì)在 commit 階段遍歷 effectList,根據(jù) effectTag 來(lái)增刪改 dom。
這個(gè)隊(duì)列叫做 effectList。
commit階段
render階段找到變化的部分,創(chuàng)建dom,打上增刪改的tag,等全部計(jì)算完之后,commit階段一次性更新到dom。
before mutation階段(執(zhí)行DOM操作前)
遍歷effectList,依次執(zhí)行:
1.處理DOM節(jié)點(diǎn)渲染/刪除后的autoFocus、blur邏輯
2.調(diào)用getSnapshotBeforeUpdate生命周期鉤子
3.調(diào)度useEffect mutation階段(執(zhí)行DOM操作)
遍歷 effectList 來(lái)更新 dom
layout階段(執(zhí)行DOM操作后)
因?yàn)檫@個(gè)階段已經(jīng)可以拿到布局信息了,會(huì)同步調(diào)用 useLayoutEffect 的回調(diào)函數(shù)。而且這個(gè)階段可以拿到新的 dom 節(jié)點(diǎn),還會(huì)更新下 ref。
原文鏈接:https://blog.csdn.net/weixin_44247866/article/details/125950721
相關(guān)推薦
- 2022-06-19 rsa詳解及例題及python算法_python
- 2022-04-14 解決Mac環(huán)境下zsh: command not found:
- 2023-02-10 python使用xlsx和pandas處理Excel表格的操作步驟_python
- 2022-09-17 docker資源控制管理Cgroup的實(shí)現(xiàn)_docker
- 2022-04-09 node sass下載失敗解決方案
- 2022-11-22 Python?flask框架定時(shí)任務(wù)apscheduler應(yīng)用介紹_python
- 2022-08-17 create-react-app項(xiàng)目配置全解析_React
- 2022-04-25 深入理解React?三大核心屬性_React
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支