【 React 】React JSX 转换成真实DOM的过程?

1. 是什么

react通过将组件编写的JSX映射到屏幕,以及组件中的状态发生了变化之后React会将这些「变化」更新到屏幕上
在前面文章了解中,JSX通过babel最终转化成React.createElement这种形式,例如:

<div>< img src="avatar.png" className="profile" /><Hello />
</div>

会被babel 转换成如下:

React.createElement("div",null,React.createElement("img", {src: "avatar.png",className: "profile"}),React.createElement(Hello, null) 
);

在转化过程中,babel在编译时会判断JSX中组件的首字母:

  • 当首字母为小写时,其被认定为原生DOM标签,createElement的第一个变量被编译为字符串
  • 当首字母为大写时,其被认定为自定义组件,createElement的第一个变量被编译为对象

最终都会通过RenderDOM.render(…)方法进行挂载,如下:

ReactDOM.render(<App />, document.getElementById("root"));

2. 过程

在react中,节点大致可以分成四个类别:

  • 原生标签节点
  • 文本节点
  • 函数组件
  • 类组件

如下所示:

class ClassComponent extends Component {static defaultProps = {color: "pink"};render() {return ( <div className="border"> <h3>ClassComponent</h3> <p className={this.props.color}>{this.props.name}</p ></div> ); }
}
function FunctionComponent(props) {return ( <div className="border">FunctionComponent<p>{props.name}</p ></div> ); 
}
const jsx = ( <div className="border"><p>xx</p > < a href=" ">xxx</ a> <FunctionComponent name=" " /><ClassComponent name=" " color="red" /></div> 
);

这些类别最终都会被转化成React.createElement这种形式
React.createElement其被调用时会传入标签类型type,标签属性props及若干子元素children ,作用是生成一个虚拟Dom对象,如下所示:

function createElement(type, config, ...children) {if (config) {delete config.__self;delete config.__source; }//源码中做了详细处理,比如过滤掉 ! key ref等const props = {...config,children: children.map(child =>typeof child === "object" ? child : createTextNode(child) ) };return {type,props}; 
}
function createTextNode(text) {return {type: TEXT,props: {children: [],nodeValue: text} }; 
}
export default {createElement
};

createElement会根据传入的节点信息进行一个判断:

  • 如果是原生标签节点,type是字符串,如div、span
  • 如果是文本节点,type就没有,这里是TEXT
  • 如果是函数组件,type是函数名
  • 如果是类组件,type是类名

虚拟DOM会通过ReactDOM.render进行渲染成真实DOM,使用方法如下:

ReactDOM.render(element,container[,callback])

当首次调用时,容器节点里的所有DOM元素都会被替换,后续的调用则会使用React的diff算法进行高效的更新
如果提供了可选的回调函数callback ,该回调将在组件被渲染或更新之后被执行render大致实
现方法如下:

function render(vnode, container) {console.log("vnode", vnode); //虚拟 DOM 对象// vnode _> nodeconst node = createNode(vnode, container);container.appendChild(node); 
}// 创建真实DOM节点
function createNode(vnode, parentNode) {let node = null;const {type, props} = vnode;if (type === TEXT) {node = document.createTextNode("");} else if (typeof type === "string") {node = document.createElement(type); } else if (typeof type === "function") {node = type.isReactComponent? updateClassComponent(vnode, parentNode) : updateFunctionComponent(vnode, parentNode); } else {node = document.createDocumentFragment(); }reconcileChildren(props.children, node);updateNode(node, props);return node;}//遍历下子 vnode 然后把子vnode-> 真实DOM 再插入到父node中
function reconcileChildren(children, node) {for (let i = 0; i < children.length; i++) {let child = children[i];if (Array.isArray(child)) {for (let j = 0; j < child.length; j++) {render(child[j], node); } } else {render(child, node); } } 
}
function updateNode(node, nextVal) {Object.keys(nextVal) .filter(k => k !== "children") .forEach(k => {if (k.slice(0, 2) === "on") { node.addEventListener(eventName, nextVal[k]); } else {node[k] = nextVal[k]; } }); 
}
//返回真实 dom节点
// 执行函数
function updateFunctionComponent(vnode, parentNode) {const {type, props} = vnode;let vvnode = type(props);const node = createNode(vvnode, parentNode);return node; 
}// dom// 先实例化,再执行render函数
function updateClassComponent(vnode, parentNode) {const {type, props} = vnode;let cmp = new type(props);const vvnode = cmp.render();const node = createNode(vvnode, parentNode);return node; 
}
export default {render
};

3. 总结

渲染流程如下所示:

  • 使用React.createElement或JSX编写React组件,实际上所有的JSX代码最后都会转换成React.createElement(…),Babel帮助我们完成了这个转换的过程。
  • createElement函数对key和ref等特殊的props进行处理,并获取defaultProps对默认props进行赋值,并且对传入的孩子节点进行处理,最终构造成一个虚拟DOM对象
  • ReactDOM.render将生成好的虚拟DOM渲染到指定容器上,其中采用了批处理、事务等机制并且对特定浏览器进行了性能优化,最终转换为真实DOM

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

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

相关文章

【ML】逻辑回归、生成式与判别式引出 3

【ML】逻辑回归、生成式与判别式引出 3 1. 逻辑回归的基本原理:2. 逻辑回归的关键点:3. 逻辑回归推导过程4. Discriminative(判别式)模型和Generative(生成式)4.1 Discriminative(判别式)模型:4.2 Generative(生成式)模型:4.3 总结差异:5. Multi-class Classific…

学习vue3第八节(自定义指令 directive)

1、自定义指令的作用&#xff1a; 自定义指令是用来操作底层DOM的&#xff0c;尽管vue推崇数据驱动视图的理念&#xff0c;但并非所有情况都适合数据驱动。自定义指令就是一种有效的补充和拓展&#xff0c;不仅仅可用于定义任何DOM操作&#xff0c;并且是可以重复使用。 自定义…

只需3步,使用Stable Diffusion无限生成AI数字人视频

基本方法 搞一张照片&#xff0c;搞一段语音&#xff0c;合成照片和语音&#xff0c;同时让照片中的人物动起来&#xff0c;特别是头、眼睛和嘴。 语音合成 语音合成的方法很多&#xff0c;也比较成熟了&#xff0c;大家可以选择自己方便的&#xff0c;直接录音也可以&#…

el-tree 设置默认展开指定层级

el-tree默认关闭所有选项&#xff0c;但是有添加或者编辑删除的情况下&#xff0c;需要刷新接口&#xff0c;此时会又要关闭所有选项&#xff1b; 需求&#xff1a;在编辑时、添加、删除 需要将该内容默认展开 <el-tree :default-expanded-keys"expandedkeys":da…

Redis 启动进程报错排查

在阿里云ECS安装了redis, systemctl 启动进程正常, redis-cli 链接正常, 准备远程链接发现无法链接上. 报错 Connection: ECS-redis > connection failed Connection: Disconnect on error: Connection error: Connection timed out 连接超时. 后来查别人的经验发现是…

递归——求阶乘

递归的基本概念 一个函数调用其自身&#xff0c;就是递归求n&#xff01;的阶乘 int Factorial(int n) {if(n0) return 1&#xff1b;return n*Factorial(n-1); } #include <iostream> using namespace std; int n; int Factorial(int n) {if(n0) return 1;return n*F…

开源模型应用落地-安全合规篇-模型输出合规性检测(三)

一、前言 为什么我们需要花大力气对用户输入的内容和模型生成的输出进行合规性检测,一方面是严格遵守各项法规要求,具体如下:互联网信息服务深度合成管理规定https://www.gov.cn/zhengce/zhengceku/2022-12/12/content_5731431.htm ​ 其次,受限于模型本身的一些缺陷,…

Trent-FPGA硬件设计课程

本课程涵盖FPGA硬件设计的基础概念和实践应用。学生将学习Verilog语言编程、数字电路设计原理、FPGA架构和开发工具的使用。通过项目实践&#xff0c;掌握FPGA设计流程和调试技巧&#xff0c;为硬件加速和嵌入式系统开发打下坚实基础。 课程大小&#xff1a;4.3G 课程下载&am…

Flutter开发入门——路由

什么是路由&#xff1f; 移动端应用开发中&#xff0c;路由技术是一个非常重要的组成部分。路由技术负责管理应用中各个页面之间的跳转、导航以及参数传递等关键功能。在移动端应用中&#xff0c;一个高效、易于维护的路由系统对于提高开发效率和用户体验具有重要意义。 Flut…

【CSS】overflow中scroll和hidden的区别是什么?

overflow: scroll; 和 overflow: hidden; 是CSS中用于处理元素内容溢出其容器时的两种不同属性值。它们的主要区别在于当内容超出指定容器的尺寸时&#xff0c;它们如何显示或隐藏内容。 如果你有一个包含大量文本或图片的元素&#xff0c;并且希望用户能够滚动查看所有内容&am…

诗意人生三层: 爱层、富足金字塔层、苟且层

海德格尔说:人充满劳绩&#xff0c;但还诗意地栖居在大地之上。论语中说&#xff0c;曾子曰:暮春三月&#xff0c;春服既成&#xff0c;童子五六人&#xff0c;冠者六七人&#xff0c;浴乎沂&#xff0c;风乎舞雩&#xff0c;咏而归。夫子喟然叹曰:吾与点也。这两个说法可视为诗…

解决wrap_socket() got an unexpected keyword argument ‘ciphers‘

看报错本以为是一个简单的传参问题&#xff0c;没想到查到盘丝洞。 # 报错信息 wrap_socket() got an unexpected keyword argument ciphers# 报错代码段 _exception_handler() def connect(self):u"""连接MySQL数据库"""self.config_connect_a…

MongoDB聚合运算符:$gt

文章目录 语法举例 $gt聚合运算符用于比较两个值&#xff0c;如果第一个大于第二个&#xff0c;返回true&#xff1b;如果第一个小于等于第二个&#xff0c;返回false。 语法 { $gt: [ <expression1>, <expression2> ] }$gt可以用来比较任何类型的值&#xff0c;…

国产化系统操作说明以及各个配置(redis、minio、kafka、mysql)

一、 中标麒麟系统安装 下载中标麒麟桌面操作系统软件&#xff08;兆芯版&#xff09;V7.0镜像文件&#xff0c;在虚拟机安装中标麒麟系统&#xff0c;安装过程不详细赘述&#xff0c;跟windows类似。国产操作系统、麒麟操作系统——麒麟软件官方网站 二、 达梦数据库安装 1…

shardingsphere-elastic-job-ui 管理界面安装

shardingsphere-elasticjob 从 3.0.0-alpha 版本开始&#xff0c;将console管理界面单独拆分出来 下载前需要 安装 maven 配置环境变量 安装 nodejs 配置环境变量 下载ui源码,安装 官方并未直接提供可执行的二进制文件,需要下载源码编译,目前发行版 3.0.2 https://github.com/…

分布式Raft原理详解,从不同角色视角分析相关状态

分布式Raft原理详解&#xff0c;从不同角色视角分析相关状态 1. CAP定理2.Raft 要解决的问题3. Raft的核心逻辑3.1. Raft的核心逻辑2.1. 复制状态机2.2. 任期 Term2.3. 任期的意义&#xff1a;逻辑时钟2.4 选举定时器 3. Leader选举逻辑4. 从节点视角查看Leader选举4.1. Follow…

springboot2.7集成es8 https方式绕过证书访问集群

版本说明 项目Valuespring-boot2.7.13es8.12.2 配置文件 spring:elasticsearch:uris: https://192.168.1.110:30920username: elasticpassword: 123456依赖文件 子pom.xml引入elasticsearch-java依赖 <dependency><groupId>co.elastic.clients</groupId>…

vue 安装脚手架报错 certificate has expired

vue 安装脚手架的时候报错&#xff0c;报错信息如下&#xff1a; 错误信息&#xff1a;npm ERR! request to https://registry.npm.taobao.org/vue%2fcli failed, reason: certificate has expired 翻译&#xff1a;npm ERR&#xff01;请求到https://registry.npm.taobao.org…

Nanya(南亚科技)DRAM芯片选型详解

一、DRAM产品选型 普通SDRAM只在时钟的上升期进行数据传输&#xff0c;DDR内存能够在时钟的上升期和下降期各传输一次数据&#xff0c;因此性能翻倍&#xff0c;被称为双倍速率同步动态随机存储器。因此DDR内存可以在与SDRAM相同的总线频率下达到更高的数据传输率。DDR是一种掉…

React 19有哪些新的变化

随着 React 19 的发布即将到来&#xff0c;它承诺带来一些令人期待的新功能。这个版本不仅仅是又一个更新&#xff0c;而是被寄予厚望&#xff0c;将重新定义我们对待 React 代码的方式。承诺提供增强的速度、效率和更流畅的编码体验&#xff0c;React 19 正准备将 Web 开发提升…