網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
熟悉 Fiber 樹(shù)結(jié)構(gòu)
我們知道,React 從 V16 版本開(kāi)始采用 Fiber 樹(shù)架構(gòu)來(lái)實(shí)現(xiàn)渲染和更新機(jī)制。
Fiber
在 React 源碼中可以看作是一個(gè)任務(wù)執(zhí)行單元,每個(gè) React Element 都會(huì)有一個(gè)與之對(duì)應(yīng)的 Fiber 節(jié)點(diǎn)。
Fiber
節(jié)點(diǎn)的核心數(shù)據(jù)結(jié)構(gòu)如下:
type Fiber = { type: any, //類型 return: Fiber, //父節(jié)點(diǎn) child: Fiber, // 指向第一個(gè)子節(jié)點(diǎn) sibling: Fiber, // 指向下一個(gè)弟弟 }
其中,以下三個(gè)屬性可以構(gòu)成 Fiber 樹(shù):
-
return
表示父 Fiber 節(jié)點(diǎn)(頂級(jí)元素沒(méi)有 return 指針) -
sibling
表示下一個(gè)兄弟 Fiber 節(jié)點(diǎn)(如果沒(méi)有下一個(gè)兄弟節(jié)點(diǎn),也就沒(méi)有這個(gè)指針) -
child
表示第一個(gè)子 Fiber 節(jié)點(diǎn)(如果沒(méi)有第一個(gè)子節(jié)點(diǎn),也就沒(méi)有這個(gè)指針)。
舉個(gè)例子,假如我們的組件結(jié)構(gòu)如下:
function App() { return ( <div> 名稱: <span>明里人</span> </div> ) }
對(duì)應(yīng)的 Fiber 樹(shù)結(jié)構(gòu)如下:
通過(guò) Fiber 樹(shù)結(jié)構(gòu)我們可以很方便的查找一個(gè)節(jié)點(diǎn)的上級(jí)、下級(jí)和同級(jí)。
接下來(lái)我們一起來(lái)看看實(shí)際的業(yè)務(wù)場(chǎng)景。
業(yè)務(wù)場(chǎng)景
有時(shí)我們會(huì)去實(shí)現(xiàn)一些任務(wù)類的需求,任務(wù)自身存在 status 狀態(tài),比如進(jìn)行中、逾期、完成狀態(tài),每個(gè)任務(wù)都可以通過(guò)完成按鈕被完成。
假設(shè)現(xiàn)在有一個(gè)「任務(wù)流」作為第一級(jí)數(shù)據(jù)出現(xiàn),它的子集由一個(gè)或多個(gè)「任務(wù)組」組成,作為第二級(jí)數(shù)據(jù)出現(xiàn),任務(wù)組的子集由一個(gè)或多個(gè)「任務(wù)」組成,作為第三級(jí)數(shù)據(jù)出現(xiàn)。
現(xiàn)有需求如下:
第三級(jí)的「任務(wù)」在完成時(shí)更新自身狀態(tài),但要考慮同步上級(jí)節(jié)點(diǎn)的狀態(tài):
- 如果當(dāng)前任務(wù)所在的任務(wù)組下所有任務(wù)都已完成,更新當(dāng)前「任務(wù)組」?fàn)顟B(tài)為完成;
- 如果所有任務(wù)組及任務(wù)都已完成,更新「任務(wù)流」?fàn)顟B(tài)為完成。
當(dāng)「任務(wù)流」處于完成狀態(tài)時(shí),UI 的體現(xiàn)可能如下:
思路:
我們要在每個(gè)任務(wù)完成后,判斷同級(jí)任務(wù)是否都已完成,若都已完成,則將父級(jí)(任務(wù)組)狀態(tài)更新為完成。
如果我們將現(xiàn)有的菜單樹(shù)結(jié)構(gòu)改造為 Fiber 樹(shù)結(jié)構(gòu),通過(guò) return
可以很方便的找到父節(jié)點(diǎn),然后通過(guò) child 和 sibling
可以很方便的查找任務(wù)組下的每一個(gè)任務(wù),決定是否更新任務(wù)組狀態(tài)。
代碼實(shí)現(xiàn)如下:
// 1. taskFlowData 任務(wù)流數(shù)據(jù) // 2. currentTaskData 當(dāng)前完成的任務(wù)數(shù)據(jù) export const getFinishedStatus = (task) => task.status === 1; // 1 代表完成 export const setFinishedStatus = (task) => task.status = 1; // 首先,將當(dāng)前任務(wù)的狀態(tài)更新 setFinishedStatus(currentTaskData); // 第一步,將現(xiàn)有數(shù)據(jù)轉(zhuǎn)換為 Fiber 樹(shù)結(jié)構(gòu) const createNode = (value, parent) => ( { value, return: parent, child: null, sibling: null } ); // 1-1. 創(chuàng)建 root fiber 根節(jié)點(diǎn),即 任務(wù)流 const rootNode = createNode(taskFlowData, undefined); let currentNode = rootNode, currentTaskNode = null; // 1-2. child 存放的是任務(wù)組,為第二級(jí)數(shù)據(jù)創(chuàng)建 Fiber 節(jié)點(diǎn) currentNode.value.child.forEach((taskGroup, taskGroupIndex) => { const node = createNode(taskGroup, rootNode); // 1-3. 建立關(guān)系 taskGroupIndex === 0 ? (currentNode.child = node) : (currentNode.sibling = node); // 1-4. 為第三級(jí)任務(wù)創(chuàng)建 Fiber 節(jié)點(diǎn) taskGroup.child.forEach((task, taskIndex) => { const childNode = createNode(task, node); taskIndex === 0 ? (node.child = childNode) : (currentNode.sibling = childNode); currentNode = childNode; // 1-5. 記錄當(dāng)前任務(wù)對(duì)應(yīng)的 Fiber 節(jié)點(diǎn) if (task.id === currentTaskData.id) currentTaskNode = currentNode; }); currentNode = node; }); // 第二步,根據(jù) Fiber 樹(shù)結(jié)構(gòu),來(lái)查找并更新?tīng)顟B(tài) let parentNode = currentTaskNode.return; while (parentNode) { let isFinished = true; // 2-1. 找到第一個(gè)任務(wù) let workInprgress = parentNode.child; // 2-2. 從第一個(gè)任務(wù)開(kāi)始,依次查看每個(gè)任務(wù)的狀態(tài) while (workInprgress) { // 2-3. 如果查找的當(dāng)前任務(wù)處于未完成狀態(tài),無(wú)需更新父級(jí)狀態(tài) if (!getFinishedStatus(workInprgress.value)) { isFinished = false; break; } workInprgress = workInprgress.sibling; } // 2-4. 更新任務(wù)組狀態(tài),再向上查找,確定是否更新任務(wù)流狀態(tài) if (isFinished) { setFinishedStatus(parentNode.value); parentNode = parentNode.return; } else { // 2-5. 任務(wù)組狀態(tài)不需要更新,直接結(jié)束 break; } }
原文鏈接:https://juejin.cn/post/7178472331247878199
相關(guān)推薦
- 2023-04-06 TypeScript?基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)哈希表?HashTable教程_其它
- 2022-06-18 C語(yǔ)言?圖文并茂詳解程序編譯過(guò)程_C 語(yǔ)言
- 2022-04-12 【debug】TypeError: mel() takes 0 positional argumen
- 2022-07-16 mybatis的相同攔截器—切面執(zhí)行的順序
- 2022-11-17 Go語(yǔ)言中常用的基礎(chǔ)方法總結(jié)_Golang
- 2022-09-24 C#/VB.NET實(shí)現(xiàn)PPT或PPTX轉(zhuǎn)換為圖像功能_C#教程
- 2022-10-04 Redis分布式鎖之紅鎖的實(shí)現(xiàn)_Redis
- 2022-06-02 Python配置文件yaml的用法詳解_python
- 最近更新
-
- 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概述快速入門(mén)
- 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)程分支