当前位置: 首页 > 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…...

基于大模型的 UI 自动化系统

基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

ES6从入门到精通:前言

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

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

基于服务器使用 apt 安装、配置 Nginx

🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...

【AI学习】三、AI算法中的向量

在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

AspectJ 在 Android 中的完整使用指南

一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...