React源碼解析18(3)------ beginWork的工作流程【mount】

摘要

OK,經過上一篇文章。我們調用了:

const root = document.querySelector('#root');
ReactDOM.createRoot(root)

生成了FilberRootNode和HostRootFilber。
并且二者之間的對應關系也已經確定。

而下一步我們就需要調用render方法來講react元素掛載在root上:

//第一節實現的jsx方法
const reactElement = jsx("div", {ref: "123",children: jsx("span", {children: "456"})
});
ReactDOM.createRoot(root).render(reactElement)

所以我們現在要實現ReactDOM.createRoot中返回的render方法。

1.update和updateQueue

首先我們思考一下,在React中,除了通過上面的render方法,會讓React組件更新。還有setState等邏輯,也可以觸發React的更新。
也就是說,我們要實現一個方法。在觸發React組件更新時調用:updateContainer。

在實現updateContainer方法前,我們先實現一套機制,用來保存更新的內容。這里可以創建一個update.js用來寫這部分內容:


//創建一個更新內容
function createUpdate(action) {return {action}
}//給FilberNode創建一個更新隊列
function createUpdateQueue() {return {shared: {pending: null}}
}//在更新隊列里添加更新內容
function enqueueUpdate(updateQueue, update) {updateQueue.shared.pending = update
}//根據更新的內容,去更新FilberNode(this.setState)
function processUpdateQueue(baseState, pendingUpdate) {const result = {memoizedState: baseState}if(pendingUpdate !== null){const action = pendingUpdate.action;//setState(() => {}) 傳入方法if(typeof action === 'function'){result.memoizedState = action(baseState);}else {//setState()result.memoizedState = action;}}return result;
}

這個時候,我們的更新相關的方法已經準備好了。現在就要開始干了。首先要在FilberNode上增加一個屬性,updateQueue用來保存更新的內容:
this.updateQueue = null;

2.updateContainer方法

現在我們開始實現updateContainer方法,該方法接受兩個參數,第一個是通過createContainer方法創建出來的FilberRootNode,第二個就是render方法傳入的ReactElement。

function createRoot(root) {const filberRootNode = createContainer(root);return {render(element) {}}
}function updateContainer(root, element) {}

在方法里,我們思考一下,如果不考慮setState的情況。第一次渲染的時候,對于最外層的FilberNode,他需要更新的內容,不就是傳入的element嗎。

所以我們通過root.current拿到最外層的FilberNode。執行對應的更新操作:

function createRoot(root) {const filberRootNode = createContainer(root);return {render(element) {updateContainer(filberRootNode, element)}}
}function updateContainer(root, element) {const hostRootFilber = root.current;const update = createUpdate(element);hostRootFilber.updateQueue = createUpdateQueue()enqueueUpdate(hostRootFilber.updateQueue, update);console.log(hostRootFilber)
}

我們將對應的節點打印出來看一下,最外層的FilberNode此時已經有了updateQueue,并且里面的內容就是對應的ReactElement
在這里插入圖片描述

3.實現beginWork

OK,現在我們最外層的FilberNode已經準備好,我們開始準備構建Filber樹。其實構建Filber樹的過程,就是創建好所有的FilberNode,并且通過return,sibling,child這三個屬性進行構建。

而表示整棵樹的結構,都存在updateQueue中的ReactElement,我們就是要通過它去創建這顆Filber樹。

現在我們不考慮有sibling屬性的情況,只考慮有return和child屬性的情況,創建beginWork方法:

function beginWork(nowFilberNode) {}function updateContainer(root, element) {const hostRootFilber = root.current;const update = createUpdate(element);hostRootFilber.updateQueue = createUpdateQueue()enqueueUpdate(hostRootFilber.updateQueue, update);beginWork(hostRootFilber);
}

我們主要要做的就是,在beginWork里面,創建好所有的fiberNode。并且找清楚他們之間的對應關系。所以我們的beginWork一定是一個遞歸的方法:
我們會判斷當前filberNode的tag:

function beginWork(nowFilberNode) {switch (nowFilberNode) {case HostRoot: {return updateHostRoot(nowFilberNode); }case HostComponent: {}case HostText: {}case FunctionComponent: {}default: {console.error('錯誤的類型')}}
}

由于第一次調用,傳入的是最外面的filberNode,所以tag應該為HostRoot。我們針對于這種情況寫一個方法updateHostRoot。

function updateHostRoot(filberNode) {const baseState = filberNode.memoizedState;const updateQueue = filberNode.updateQueue;const pending = updateQueue.shared.pending;updateQueue.shared.pending = null;const { memoizedState } = processUpdateQueue(baseState, pending);filberNode.memoizedProps = memoizedState;const nextChildren = filberNode.memoizedProps;console.log(nextChildren);console.log(filberNode);
}

對于首屏渲染,我們知道對于FilberNode來說,更新的內容就是他的子節點。所以我們更新好FilebrNode的updateQueue屬性和memoizedState,memoizedProps屬性后。

可以直接拿到它的子節點nextChildren,不過這個節點是ReactElement類型,我們要將它轉換成FilberNode,所以我們還需要一個方法:reconcilerChildren。

function reconcileChildren(element) {let tag;if(element.$$typeof === REACT_ELEMENT_TYPE){tag = HostComponent;}else if(typeof element === 'string'){}return new FilberNode(tag, element.props, element.key)}

我們創建好的子FilberNode,用element的props初始化FilberNode的penddingProps。
這個時候我們在updateHostRoot中調用該方法,并將子FilberNode打印出來。

function updateHostRoot(filberNode) {/*** 其他代碼**/const newFilberNode = reconcileChildren(nextChildren);filberNode.child = newFilberNode;newFilberNode.return = filberNodeconsole.log(newFilberNode);beginWork(newFilberNode)
}

可以看到子FilberNode和父節點的關系已經更新好,同時也將自己的ReactElement放在了pendingProps里。

在這里插入圖片描述

Ok,對于HostRoot類型(最外層的FilberNode),我們有了updateHostRoot方法處理,那對于HostComponent類型,我們自然也需要updateHostComponent方法:

function updateHostComponent(filberNode) {const nextChildren = filberNode.pendingProps.children;const newFilberNode = reconcileChildren(nextChildren);filberNode.child = newFilberNode;newFilberNode.return = filberNode;beginWork(newFilberNode)
}

同樣的,對于文本類型的節點,自然也不需要去給它創建FilberNode。這里面我們不做處理就好。

到這里,我們就已經簡單的處理了只有child和return屬性的Filber樹,最終也可以打印出來這顆樹的樣子:
在這里插入圖片描述
可以看到每個FilberNode都具有child和return兩個屬性。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/36377.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/36377.shtml
英文地址,請注明出處:http://en.pswp.cn/news/36377.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【JavaEE進階】SpringBoot 日志

文章目錄 一. 日志有什么用?二. 自定義日志打印1. 日志的使用與打印 三. 日志級別1. 日志級別有什么用?2. 日志級別的分類及使用 四. 日志持久化五. 更簡單的日志輸出---Lombok1. Lombok的使用2. lombok原理解釋2.1 Lombok更多注解說明 一. 日志有什么用? 在Java中&#xf…

【結構型設計模式】C#設計模式之外觀模式

題目描述: 假設你正在開發一個音樂播放器應用程序,該應用程序需要與多個子系統進行交互,包括音頻解碼、音量控制和播放控制等。請使用外觀模式設計一個音樂播放器的外觀類,并實現相應的子系統類。 要求: 創建一個外觀…

【gogogo專欄】指針

go語言指針 為什么需要指針指針使用實例值傳遞地址傳遞多級指針 為什么需要指針 作為一個大學劃水,畢業一直寫java的程序員來說,多多少少對于指針有點陌生,由于近期需要轉go,正好學到指針這里,就來探究下指針的使用場景…

ThreadLocal詳解

ThreadLocal詳解 一、故事背景二、知識點主要構成1、什么是ThreadLocal?2、ThreadLocal的基本使用內存泄漏問題引用類型:強引用:軟引用弱引用虛引用 ThreadLocal內存泄漏原因 三、總結提升 一、故事背景 最近在學習并發編程相關內容&#xf…

pycharm 安裝庫

這是另一種方式。 搜索到的安裝庫的方式多數是:在桌面上winR鍵運行終端,輸入命令,安裝不了,發現安裝不了。 1、打開pycharm; 2、軟件下部的Terminal終端(需要運行一個代碼才能出現,任何代碼都可)&#xf…

Es、kibana安裝教程-ES(二)

上篇文章介紹了ES負責數據存儲,計算和搜索,他與傳統數據庫不同,是基于倒排索引來解決問題的。Kibana是es可視化工具。 分布式搜索ElasticSearch-ES(一) 一、ElasticSearch安裝 官網下載地址:https://www…

[C語言] 指針

1. 指針是什么 2. 指針和指針類型 3. 野指針 4. 指針運算 5. 指針和數組 6. 二級指針 7. 指針數組 目錄 1. 指針是什么? 2. 指針和指針類型 2.1 指針-整數 2.2 指針的解引用 3. 野指針 3.1 野指針成因 3.2 如何規避野指針 4. 指針運算 4.1 指針…

不用技術代碼,分班查詢系統怎么做?

暑假即將結束,新學期開始將面臨分班信息公布的工作!對于分班信息公布,涉及到學生的個人信息,包括姓名、學號、班級等。在發布這些信息時,必須確保數據的保密性,防止未經授權的人員獲取到學生的個人信息。因…

對字符串中所有單詞進行倒排-C語言/Java

描述 輸入一個字符串,輸出字符串中單詞的倒序。 要求 構成單詞的字符只有26個大寫或小寫英文字母。非構成單詞的字符均視為單詞間隔符;倒排后的單詞間隔符以一個空格表示;如果原字符串中相鄰單詞間有多個間隔符時,倒排轉換后也只…

docker的服務/容器缺少vim問題

背景/問題: docker的服務/容器缺少vim問題 bash: vim: command not found 在docker的mysql服務中安裝Vim 1、執行apt-get update root6d8d17e320a0:/# apt-get update問題:文件下載失敗 Err:1 http://security.debian.org/debian-security buster/updates InRe…

【Linux】程序地址空間

程序地址空間 首先引入地址空間的作用什么是地址空間為什么要有地址空間 首先引入地址空間的作用 1 #include <stdio.h>2 #include <unistd.h>3 #include <stdlib.h>4 int g_val 100;6 int main()7 {8 pid_t id fork();9 if(id 0)10 {11 int cn…

自動方向識別式 LSF型電平轉換芯片

大家好&#xff0c;這里是大話硬件。 今天這篇文章想分享一下電平轉換芯片相關的內容。 其實在之前的文章分享過一篇關于電平轉換芯片的相關內容&#xff0c;具體可以看鏈接《高速電路邏輯電平轉換設計》。當時這篇文章也是分析的電平轉換芯片&#xff0c;不過那時候更多的是…

矩陣的轉置

題目&#xff1a; 給你一個二維整數數組 matrix&#xff0c; 返回 matrix 的 轉置矩陣 。 示例 1&#xff1a; 輸入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 輸出&#xff1a;[[1,4,7],[2,5,8],[3,6,9]]class Solution(object):def transpose(self, matrix):"&q…

JMeter 的并發設置教程

JMeter 是一個功能強大的性能測試工具&#xff0c;可以模擬許多用戶同時訪問應用程序的情況。在使用 JMeter 進行性能測試時&#xff0c;設置并發是非常重要的。本文將介紹如何在 JMeter 中設置并發和查看報告。 設置并發 并發是在線程組下的線程屬性中設置的。 線程數&#…

3.解構賦值

解構賦值是一種快速為變量賦值的簡潔語法&#xff0c;本質上仍然是為變量賦值。 3.1數組解構 數組解構是 將數組的單元值快速批量賦值給一系列變量 的簡潔語法 1.基本語法: &#xff08;1&#xff09;賦值運算符左側的[ ]用于批量聲明變量&#xff0c;右側數組的單元值將被賦…

前后端分離------后端創建筆記(04)前后端對接

本文章轉載于【SpringBootVue】全網最簡單但實用的前后端分離項目實戰筆記 - 前端_大菜007的博客-CSDN博客 僅用于學習和討論&#xff0c;如有侵權請聯系 源碼&#xff1a;https://gitee.com/green_vegetables/x-admin-project.git 素材&#xff1a;https://pan.baidu.com/s/…

【JavaEE進階】Bean 作用域和生命周期

文章目錄 一. 關于Bean作用域的實例1. lombok2. 實例代碼 二. 作用域定義1. Bean的六種作用域2. 設置作用域 三. Spring 執行流程和 Bean 的生命周期1. Spring 執行流程2. Bean生命周期 一. 關于Bean作用域的實例 注意在此例子中需要用到lombok 1. lombok lombok是什么? Lo…

【C#】判斷打印機共享狀態

打印機共享狀態 /// <summary>/// 打印機共享狀態/// </summary>public enum PrinterShareState{/// <summary>/// 無打印機/// </summary>None -1,/// <summary>/// 未共享/// </summary>NotShare 0,/// <summary>/// 已共享/// …

soap通信2

首先&#xff0c;定義一個XSD&#xff08;XML Schema Definition&#xff09;來描述你的數據結構。在你的Maven項目的src/main/resources目錄下&#xff0c;創建一個名為schemas的文件夾&#xff0c;并在其中創建一個名為scriptService.xsd的文件&#xff0c;內容如下&#xff…

【kubernetes】調度約束

目錄 調度約束 Pod 啟動典型創建過程如下 調度過程 指定調度節點 查看詳細事件&#xff08;發現未經過 scheduler 調度分配&#xff09; 獲取標簽幫助 需要獲取 node 上的 NAME 名稱 給對應的 node 設置標簽分別為 ggls 和 gglm 查看標簽 修改成 nodeSelector 調度方…