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

news2024/11/28 20:57:19

摘要

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 = filberNode
  console.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.coloradmin.cn/o/858081.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

途乐证券-保险消费者信心延续恢复态势 健康险、意外险增购意愿上

今年以来,稳妥顾客决心延续调整康复态势。我国稳妥保证基金有限责任公司(以下简称“稳妥保证基金公司”)近来发布数据显现,2023年二季度,我国稳妥顾客决心指数为67.7,环比下降3.6,同比上升1.2&a…

一文读懂c++语言

一文读懂C语言 C的发展C的设计目标C的特性C的挑战 C的发展 C是一种通用的、高级的编程语言,它是C语言的扩展。C由Bjarne Stroustrup于1983年首次引入,并在之后的几十年中不断发展壮大。C被广泛应用于各种领域,包括系统开发、游戏开发、嵌入式…

keil显示中文代码正常,但是编译中文乱码的问题

Configuration---Editor---Encoding,ANSI或是UTF8,总之这2个,是A就改为U,是U就改为A。 MDK5中文编译乱码,不是显示乱码哦。

Android Camera预览画面变形问题

csdn 问题 安卓camera1在预览时,预览画面看起来被拉伸了. 如图,圆形的盖子,变成椭圆形了. 代码 默认流程,如下为大致的打开摄像头并进行预览显示的代码 private Camera mCamera null; private Surfa…

YOLOv5入门

模型检测 关键参数 weights:训练好的模型文件 source: 检测的目标,可以是单张图片、文件夹、屏幕或者摄像头等 conf-thres: 置信度闯值,越低框越多,越高框越少 iou-thres: IOU闻值,越低框越少,越少框越多 torch.hu…

windows安装apache-jmeter-5.6.2教程

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

vivo 场景下的 H5无障碍适配实践

作者:vivo 互联网前端团队- Zhang Li、Dai Wenkuan 随着信息无障碍的建设越来越受重视,开发人员在无障碍适配中也遇到了越来越多的挑战。本文是笔者在vivo开发H5项目做无障碍适配的实践总结。本文主要介绍了在前端项目中常用的无障碍手势和无障碍属性&am…

kubeadm安装

master(2C/4G,cpu核心数要求大于2) 192.168.223.71 node01(2C/2G) 192.168.223.72 node02(2C/2G) 192.168.223.73…

validator入门

validator中文文档地址和英文地址 https://docs.jboss.org/hibernate/validator/4.2/reference/zh-CN/html/validator-gettingstarted.html https://docs.jboss.org/hibernate/validator/6.0/reference/en-US/html_single/#preface自定义hibernate-validator校验 工具类Valid…

基于SOM神经网络的柴油机故障诊断

1.案例背景 1.1 SOM神经网络概述 自组织特征映射网络(Self - Organizing Feature Map,SOM)也称Kohonen网络,它是由荷兰学者Teuvo Kohonen于1981年提出的。该网络是一个由全连接的神经元阵列组成的无教师、自组织、自学习网络。Kohonen认为,处于空间中不同区域的神经元有着不同…

别克VELITE 5增程式混动电动汽车变速箱解析

别克VELITE 5增程式电动汽车智能无极变速箱采用双电机双行星轮组的设计,相比其他技术具有更高的自由度,两个点击可同时参与驱动或单独发电,在内部集成的TPIM 模块的控制下,发动机与点击可同时100%参与驱动,变速范围更加…

嘉楠勘智k230开发板上手记录(二)--hello world

上次成功在k230上烧录sdk,这次准备实现hello world和ssh scp远程k230 主要是按照K230 SDK 基础教程的K230_实战基础篇_hello_world.md 一、PC连接k230 1. 初步准备 首先下载串口工具PuTTY,这个我个人感觉比较方便。 准备两根USB type-C数据线&#…

Vue + MapBox快速搭建

一、说明: 1.mapbox-gl自2.0版本开始不再开源,需要用户在官网申请key使用。 2.maplibre GL JS是一个开源库,它起源于 mapbox-gl-js 的开源分支。该库的初始版本(1.x)旨在替代Mapbox的OSS版本。简单来说maplibre是mapb…

老师如何设计一个实用的分班查询系统?

暑期过后,学校将迎来分班工作。有些是小升初需要分班,有些是高一升高二需要分班。对于老师来说,直接将分班结果发送到班级群,家长找不到结果时会发送信息询问,放假期间老师也需要时刻盯着手机。 不过,聪明…

公网环境Windows系统,远程桌面控制树莓派《内网穿透》

远程桌面控制树莓派,我们可以用xrdp协议来实现,它内部使用的是windows远程桌面的协议。我们只需要在树莓派上安装xrdp,就可以在同个局域网下远程桌面控制树莓派。 而如果需要在公网下远程桌面控制树莓派,可以通过cpolar内网穿透&a…

C语言笔试训练【第七天】

文章目录 🪒1、以下对C语言函数的有关描述中,正确的有【多选】( )🧯2、在C语言中,以下正确的说法是( )🪪3、在上下文及头文件均正常的情况下,下列代码的输出是…

跨境干货|TikTok变现的9种方法

在这个流量为王的时代,哪里有流量,哪里就有商机。TikTok作为近几年最火爆的社媒平台之一,在全球范围都具有一定的影响力。随着TikTok Shop等商务功能加持上线,更是称为跨境电商的新主场之一。 在这样的UGC平台,想要变…

VM官网下载VM workstations pro

官网下载https://www.vmware.com/products.html 此处需要注册账号下载,用邮箱注册一个一账号就可以了,下载就完成了

15.2 【Linux】仅执行一次的工作调度

15.2.1 atd 的启动与 at 运行的方式 要使用单一工作调度时,我们的 Linux 系统上面必须要有负责这个调度的服务,那就是 atd 。 不过并非所有的 Linux distributions 都默认会把他打开的,所以,某些时刻我们必须要手动将他启用才行。…

巨人互动|Meta海外户Meta事件管理工具后台介绍

Meta事件管理工具后台是一种强大的工具,用于管理和跟踪项目中的各种事件,它提供了一个集中化的平台,帮助团队成员有效地处理和协调任务,确保项目能够按时完成并达到预期目标。本文小编将介绍一下Meta事件管理工具后台的功能和特点…