当前位置: 首页 > news >正文

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两个属性。

相关文章:

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

摘要 OK,经过上一篇文章。我们调用了: const root document.querySelector(#root); ReactDOM.createRoot(root)生成了FilberRootNode和HostRootFilber。 并且二者之间的对应关系也已经确定。 而下一步我们就需要调用render方法来讲react元素挂载在ro…...

JAVA SpringBoot 项目 多线程、线程池的使用。

1.1 线程: 线程就是进程中的单个顺序控制流,也可以理解成是一条执行路径 单线程:一个进程中包含一个顺序控制流(一条执行路径) 多线程:一个进程中包含多个顺序控制流(多条执行路径&#xff0…...

【数据结构与算法】动态规划算法

动态规划算法 应用场景 - 背包问题 背包问题:有一个背包,容量为 4 磅,现有如下物品: 物品重量价格吉他(G)11500音响(S)43000电脑(L)32000 要求达到的目标…...

离线安装vscode插件,导出 Visual Studio Code 的扩展应用,并离线安装

在没有网络的情况下,如何安装vscode插件 1.使用之前电脑安装过的插件包 Visual Studio Code 的扩展应用安装位置在文件夹 .vscode/extensions 下。不同平台,它位于: Windows %USERPROFILE%\.vscode\extensions Mac ~/.vscode/extensions L…...

【ChatGPT 指令大全】怎么使用ChatGPT辅助程式开发

目录 写程式 解读程式码 重构程式码 解 bug 写测试 写 Regex 总结 在当今快节奏的数字化世界中,程式开发变得越来越重要和普遍。无论是开发应用程序、网站还是其他软件,程式开发的需求都在不断增长。然而,有时候我们可能会遇到各种问题…...

涂色

蜀山区2021年信息学竞赛试题 题目描述 Description 小李喜欢写日记,为了有效区分每题记录的内容,他循环使用七种不同颜色的笔在日记本上记录每天发生的事情,循环次序分别为Red,Orange,Yellow,Green,Blue,Cyan,Purple,由于近期工作繁忙&…...

微服务——数据同步

问题分析 mysql和redis之间有数据同步问题,ES和mysql之间也有数据同步问题。 单体项目可以在crud时就直接去修改,但在微服务里面不同的服务不行。 方案一 方案二 方案三 总结 导入酒店管理项目 倒入完成功启动后可以看见数据成功获取到了 声明队列和…...

MySQL 手机选号(AABB、ABCD、DCBA、AAA),SQL SERVER 手机选号(AABB、ABCD、DCBA、AAA),通过规则查询靓号

先上SQL SERVER: create table plat_uidlist(Uidd varchar(15) , Areaid int , State int)insert into plat_uidlist values(2335435 ,8 ,0 ) insert into plat_uidlist values(2335436 ,8 ,1 ) insert into plat_uidlist values(2335437 ,2 ,2 ) insert into plat…...

【server组件】——mysql连接池的实现原理

目录 1.池化技术 2.数据库连接池的定义 3.为什么要使用连接池 4. 数据库连接池的运行机制 5. 连接池与线程池的关系 6. CResultSet的设计 6.1构造函数 7. CDBConn的设计 6.1.构造函数 6.2.init——初始化连接 8.数据库连接池的设计要点 9.接口设计 9.1 构造函数 …...

DSP开发:串口sci的发送与接收实现

DSP开发:串口sci的发送与接收实现 文章目录 DSP开发:串口sci的发送与接收实现串口配置串口SCI初始化详细分析串口SCI使用 串口配置 /*--------------------------------------------scia----------------------------*/ /*----------------------------…...

实训一 :Linux的启动、关机及登录

实训一 :Linux的启动、关机及登录 2017 年 2 月 22 日 今日公布 实训目标 完成本次实训,将能够: 描述Linux的开机过程。在图形模式和文本模式下登录Linux。关闭和重启Linux 实训准备 一台已安装RHEL6的虚拟计算机,Linux虚拟…...

Redis分布式锁问题

1、业务单机情况下 问题:并发没有加锁导致线程安全问题。 解决方法:加锁处理,如lock、synchronized 仍有问题:业务分布式情况下,代码级别加锁已经无效。需要借助第三方组件,如redis、zookeeper。 2、业务分…...

windows安装apache-jmeter-5.6.2教程

目录 一、下载安装包(推荐第二种) 二、安装jmeter 三、启动jmeter 一、下载安装包(推荐第二种) 1.官网下载:Apache JMeter - Download Apache JMeter 2.百度云下载:链接:https://pan.baidu.…...

密码检查-C语言/Java

描述 小明同学最近开发了一个网站,在用户注册账户的时候,需要设置账户的密码,为了加强账户的安全性,小明对密码强度有一定要求: 1. 密码只能由大写字母,小写字母,数字构成; 2. 密码不…...

基于Matlab实现心电信号小波特征提取和对应疾病识别仿真(附上源码+数据集)

本文基于Matlab平台,研究了心电信号的小波特征提取方法,并应用于心电信号疾病识别仿真实验中。首先,介绍了心电信号的基本特征和常见的心电疾病。然后,详细阐述了小波变换的原理和方法,并提出了一种基于小波分解和小波…...

第五十二天

HTML5 ●MathML 是数学标记语言,是一种基于XML(标准通用标记语言的子集)的标准,用来在互联网上书写数学符号和公式的置标语言。 ●拖放 拖放是一种常见的特性,即抓取对象以后拖到另一个位置。 在 HTML5 中&#xf…...

爬虫练手项目——获取龙族小说全文

网站信息 目标网站信息如下:包含了龙族1-5全部内容 代码 import requests from bs4 import BeautifulSoup import os import timeheaders {User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Sa…...

OpenCV图像处理——几何变换

目录 图像缩放图像平移图像旋转图像的仿射变换透射变换图像金字塔 图像缩放 cv.resize(src,dsize,fx0,fy0,interpolationcv2.INTER_LINEAR)import numpy as np import cv2 as cv import matplotlib.pyplot as pltkidscv.imread(./汪学长的随堂资料/4/图像操作/dog.jpg) plt.im…...

Apache JMeter:完全指南

Apache JMeter 是一款开源的性能测试工具,可以用于测试 Web 应用程序、FTP 服务器、数据库等各种类型的服务器。本文将以 JMeter 5.5 为例介绍 JMeter 的使用方法。 下载和安装 由于 JMeter 是使用 Java 开发的,因此在运行之前必须先安装 JDK。您可以在…...

js obj 生成java 实体 entity

js obj 生成java 实体 entity D:\proj\js\js-util\gen_java.js 文档:js obj 生成java 实体 entity.note 链接:http://note.youdao.com/noteshare?idc61fbd4b8684dc41c8c9ab42107d5f87&subE7484F5F277041578A2E58FDC6320BA6 添加链接描述 class S…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

ES6从入门到精通:前言

ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

什么是Ansible Jinja2

理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具&#xff0c;可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板&#xff0c;允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板&#xff0c;并通…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...