React源码解析之createElement和render方法

news2025/1/31 2:52:32

参考资料

请注意,这是React16.8的源码解析,当然他完全可以作为你阅读源码的参考,他还没有落后。

Step1

开始之前,要先了解一个知识点⬇️
我们都知道,要在JSX中写React语法,那为什么不能在js文件中写呢?也可以,但是你要使用相关的Babel转一下react语法,转成JS认识的语法。

换句话说,必须得有Babel将JSX转成JS,你的代码才能正常运行。

Step2

看一下这段代码

const element = <h1 title="foo">Hello</h1>

JS肯定无法识别这句话,但是用到Step1中的Babel转一下,JS就能识别了,那么转成了什么样子呢?让我们打印一下element⬇️
在这里插入图片描述
没错,变成了一个对象,记住这个type和props

你可以把type理解成标签名,props理解成这个node的所有属性

要注意:这个对象并不是Babel生成的,是React.createElement的方法生成的。
Babel只是告诉node要把这个react语法通过React.createElement方法转成js能识别的对象(这是我猜的)

Step3

让我们来重写一下精简版的React的createElement方法

function createTextElement(text) {
  return {
    type: "TEXT_ELEMENT",
    props: {
      nodeValue: text,
      children: []
    }
  };
}

function createElement(type, props, ...children) {
  return {
    type,
    props: {
      ...props,
      children: children.map((chil) =>
        typeof chil === "object" ? chil : createTextElement(chil)
      )
    }
  };
}

这里的文本(TEXT_ELEMENT)对象好像和现在的略有不同(我没有看到现在React的TEXT_ELEMENT),不过它也不影响源码阅读。

让我们看几个dom节点生成了怎样的对象吧⬇️

const element = <h1 title="foo">Hello</h1>
⬇️⬇️⬇️
const element = React.createElement(
  "h1",
  { title: "foo" },
  "Hello"
)
⬇️⬇️⬇️
const element = {
  type: "h1",
  props: {
    title: "foo",
    children: "Hello",
  },
}
const element = (
  <div id="foo">
    <a>bar</a>
    <b />
  </div>
)
⬇️⬇️⬇️
const element = React.createElement(
  "div",
  { id: "foo" },
  React.createElement("a", null, "bar"),
  React.createElement("b")
)
⬇️⬇️⬇️
const element = {
  type: "div",
  props: {
    title: "id",
    children: [
		{
			type:"a",
			props:{
				children:"bar"
			}
		},
		{
			type:"b",
			props:{}
		},
	],
  },
}

Step4

接下来就是render了,将渲染完的对象element,渲染成真正的dom节点⬇️。
让我们重写一下精简版的ReactDom的render的方法,在18的版本中移除了它,也可以继续参考。

function render(element, container) {
  const { type, props } = element;
  // 根据type创建节点
  const dom =
    type !== "TEXT_ELEMENT"
      ? document.createElement(type)
      : document.createTextNode("");

  // 将属性添加到节点
  Object.keys(props)
    .filter((key) => key !== "children")
    .forEach((name) => (dom[name] = props[name]));

  // 递归增加子节点
  props.children.forEach((chil) => render(chil, dom));
  // 添加节点
  container.appendChild(dom);
}

Step5

接下来,你只需要像初始化react项目时,把你的App挂载到root上就可以了。

const element = (
  <div style="background: salmon">
    <h1>Hello World</h1>
    <h2 style="text-align:right">from Didact</h2>
  </div>
);

const container = document.getElementById("root");
Lee.render(element, container);

你可以看一下这个sandbox来验证你的学习成果。
这是我的完整练习代码⬇️

function createTextElement(text) {
  return {
    type: "TEXT_ELEMENT",
    props: {
      nodeValue: text,
      children: []
    }
  };
}

function createElement(type, props, ...children) {
  return {
    type,
    props: {
      ...props,
      children: children.map((chil) =>
        typeof chil === "object" ? chil : createTextElement(chil)
      )
    }
  };
}

function render(element, container) {
  const { type, props } = element;
  const dom =
    type !== "TEXT_ELEMENT"
      ? document.createElement(type)
      : document.createTextNode("");

  Object.keys(props)
    .filter((key) => key !== "children")
    .forEach((name) => (dom[name] = props[name]));

  props.children.forEach((chil) => render(chil, dom));
  container.appendChild(dom);
}

const Lee = {
  render,
  createElement
};

/** @jsx Lee.createElement */
const element = (
  <div style="background: salmon">
    <h1>Hello World</h1>
    <h2 style="text-align:right">from Didact</h2>
  </div>
);

const container = document.getElementById("root");
Lee.render(element, container);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/413416.html

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

相关文章

微搭使用笔记(六) 通过源码组件实现小程序端地图

前言 微搭官方提供了大量常用组件&#xff0c;但由于微搭本身也是在不断地完善过程中&#xff0c;有些组件还是没有提供&#xff0c;但同时微搭允许用户自定义组件并在应用中使用。 实际场景是这样的&#xff0c;我们需要一个地图页面在上面展示已知设备的信息和位置&#xf…

CentOS7 虚拟机 双网卡绑定

一、网卡绑定模式 模式类型特点mode0round-robin&#xff08;平衡轮询策略&#xff09;基于per packet方式&#xff0c;轮询往每条链路发送报文。提供负载均衡和容错的能力&#xff0c;当有链路出问题&#xff0c;会把流量切换到正常的链路上。交换机端需要配置聚合口。mode1a…

Linux网络编程(四)——UDP通信

目录 0x01 UDP协议 一、UDP通信简介以及接口 二、UDP的接口 三、UDP收发例程 0x02 广播 一、设置广播数据函数接口 二、广播代码实现 0x03 组播&#xff08;多播&#xff09; 一、组播地址 二、设置组播函数接口 三、代码实现 0x01 UDP协议 一、UDP通信简介以及接口…

《Netty》从零开始学netty源码(三十六)之ChannelConfig

ChannelConfig 在前面创建NioServerSocketChannel的 构造函数中&#xff0c;最后一步创建了channel属性的配置类NioServerSocketChannelConfig&#xff0c;本文详细分析下该类&#xff0c;先看下其类结构图。 类结构图 服务端使用的NioServerSocketChannelConfig&#xff0c;…

ELK日志分析系统+zookeeper

ELK日志分析系统zookeeper一、zookeeper简介1、zookeeper概念2、zookeeper数据结构二、zookeeper工作机制1、zookeeper特点2、zookeeper应用场景三、zookeeper集群部署1、安装前先关闭防火墙 核心防护2、安装JDK3、安装zookeeper4、修改配置文件5、创建数据目录和日志目录&…

JavaEE-网络原理之UDP协议

目录UDP报文结构UDP的特点无连接不可靠面向数据报缓冲区基于UDP的应用层协议UDP报文结构 报头大小为8个字节. 16位源端口号与16位目的端口号: 16个比特位可表示65536个端口号,分别为0-65535,其中1-1023为为专属端口号,用来为一些知名服务器提供服务,例如: HTTP服务器专属端口号…

【CSS】课程网站 网格商品展示 模块制作 ③ ( 清除浮动需求 | 没有设置高度的盒子且内部设置了浮动 | 使用双伪元素清除浮动 )

文章目录一、清除浮动需求 ( 没有设置高度的盒子且内部设置了浮动 )二、清除浮动代码示例一、清除浮动需求 ( 没有设置高度的盒子且内部设置了浮动 ) 如果盒子没有设置高度 , 并且盒子中还设置了浮动 , 如上一篇博客 【CSS】课程网站 网格商品展示 模块制作 ② ( 网格商品展示盒…

溯源反制(windows)

痛点&#xff1a; windows服务器被恶意入侵出现遭受挖矿&#xff0c;在没有专业的安全溯源反制的工具如何排查系统异常文件精准找出异常程序呢&#xff1f; 这样的吗 使用开源火绒、后门工具、D_盾_web查杀工具、360安全卫士进行全局查杀搜寻异常文件程序。 在应急响应中&am…

SpringBoot默认包扫描机制与默认配置文件

文章目录一、SpringBoot默认包扫描机制 - 示例二、SpringBoot默认扫描包机制 - 原理三、SpringBoot手动扫描包机制 - 原理&示例四、ComponentScan与MapperScan五、SpringBoot默认配置文件一、SpringBoot默认包扫描机制 - 示例 默认情况下&#xff0c;扫描启动类同级及其子…

护眼灯到底有没有用?盘点口碑销量好的护眼台灯

有一定的护眼作用。很多人认为护眼灯是智商税&#xff0c;想法并不正确&#xff0c;市面上品质好的护眼灯&#xff0c;光线是通过特殊处理过的&#xff0c;色温、亮度、光线均匀度、显色性都贴合人眼&#xff0c;并且能够护眼的效果。 我们在挑选护眼灯时&#xff0c;在室内环…

【MyBatis Plus】003 -- 配置(基本、进阶、DB策略) 条件构造器

目录 4、配置 4.1 基本配置 4.1.1 configLocation &#xff08;MyBatis 配置文件位置&#xff09; 4.1.2 mapperLocations&#xff08;MyBatis Mapper 所对应的 XML 文件位置&#xff09; 4.1.3 typeAliasesPackage &#xff08;别名包扫描路径&#xff09; 4.2 进阶配置 4.2.1…

代码随想录算法训练营第五十七天 | 647. 回文子串、516. 最长回文子序列、动态规划总结

647. 回文子串 动规五部曲 1、确定dp数组&#xff08;dp table&#xff09;以及下标的含义 在判断字符串S是否为回文时&#xff0c;如果知道 s[1]&#xff0c;s[2]&#xff0c;s[3] 这个子串是回文的&#xff0c;那么只需要比较 s[0]和s[4]这两个元素是否相同&#xff0c;如果…

NodeJS Cluster模块基础教程

Cluster简介 默认情况下&#xff0c;Node.js不会利用所有的CPU&#xff0c;即使机器有多个CPU。一旦这个进程崩掉&#xff0c;那么整个 web 服务就崩掉了。 应用部署到多核服务器时&#xff0c;为了充分利用多核 CPU 资源一般启动多个 NodeJS 进程提供服务&#xff0c;这时就…

【java】面向对象的编程基础

文章目录面向对象的编程基础定义方法重载执行顺序静态变量和方法加载顺序包和访问控制类的封装object类方法重写抽象类接口枚举类面向对象的编程基础 定义 public class person { String name; int age; char sex; person(String name,int age,char sex) {this.ageage;this.…

【华为OD机试】1043 - 从单向链表中删除指定值的节点

文章目录一、题目&#x1f538;题目描述&#x1f538;输入输出&#x1f538;样例1&#x1f538;样例2二、代码参考作者&#xff1a;KJ.JK&#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &am…

计算机网络笔记(横向)

该笔记也是我考研期间做的整理。一般网上的笔记是按照章节纪录的&#xff0c;我是按照知识点分类纪录的&#xff0c;大纲如下&#xff1a; 文章目录1. 各报文1.1 各报文头部详解1.2 相关口诀2. 各协议2.1 各应用层协议使用的传输层协议与端口2.2 各协议的过程2.2.1 数据链路层的…

零死角玩转stm32中级篇2-IIC总线

本篇博文目录:一.IIC基础知识1.什么是IIC总线2.IIC总线和串口有什么区别3.IIC总线是怎么实现多机通信4.仲裁是什么5.如果当前有一个从机进行了IIC通信又来了一个优先级高的从机&#xff0c;这时会打断前一个通信吗?6.IIC是怎么保证地址的唯一性7.在IIC总线协议中&#xff0c;规…

走进小程序【六】微信小程序架构之【视图层】万字详解

文章目录&#x1f31f;前言&#x1f31f;小程序架构&#x1f31f;视图层 View&#x1f31f;WXML&#x1f31f;数据绑定&#x1f31f;列表渲染&#x1f31f;条件渲染&#x1f31f;模板&#x1f31f;WXSS&#x1f31f;尺寸单位&#x1f31f;样式导入&#x1f31f;内联样式&#x…

VIM 编辑器使用教程

我们如果要在终端模式下进行文本编辑或者修改文件就可以使用 VI/VIM 编辑器&#xff0c;Ubuntu 自带了 VI 编辑器&#xff0c;但是 VI 编辑器对于习惯了 Windows 下进行开发的人来说不方便&#xff0c;比如竟然 不能使用键盘上的上下左右键调整光标位置。因此我推荐大家使用 V…

PADS-按键、蜂鸣器、继电器PCB封装设计

1 按键PCB封装设计 1.1 查看元件手册, 得知焊盘尺寸&#xff0c;同时需要观察按键&#xff0c;用丝印来进行表示。 1.2 进入PADS-Layout 无模命令UMM G0.254 GD0.254进行设计 放置一个表贴端点&#xff0c;更改矩形尺寸&#xff0c;同时计算与原点的距离&#xff0c;这里我们按…