React: JSX生成真实DOM结点
在上一篇文章中,我们介绍了 Babel 是如何将 JSX 代码编译成可执行代码的,随后也实现了一个自己的解析器,模拟了 Babel 编译的过程。
现在我们再来回顾一下,假定有如下业务代码:
const style = {
color: 'red',
fontSize: '20px',
};
const greet = function (name) {
return `hello ${name}`;
};
const App = (
<div className="container">
<p style={style}>saying {greet('scott')} hah</p>
<div>
<p>this is jsx-like code</p>
<i className="icon"/>
<p>parsing it now</p>
<img className="icon"/>
</div>
<input type="button" value="i am a button"/>
<em/>
</div>
);
console.log(App);
ReactDOM.render(App, document.getElementById('root'));
经过编译之后,会生成下面的可执行代码:
var style = {
color: 'red',
fontSize: '20px'
};
var greet = function greet(name) {
return 'hello ' + name;
};
var App = React.createElement(
'div',
{ className: 'container' },
React.createElement(
'p',
{ style: style },
'saying ',
greet('scott'),
' hah'
),
React.createElement(
'div',
null,
React.createElement(
'p',
null,
'this is jsx-like code'
),
React.createElement('i', { className: 'icon' }),
React.createElement(
'p',
null,
'parsing it now'
),
React.createElement('img', { className: 'icon' })
),
React.createElement('input', { type: 'button', value: 'i am a button' }),
React.createElement('em', null)
);
console.log(App);
ReactDOM.render(App, document.getElementById('root'));
引入所需的React库:
<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<script src="index.js"></script>
</body>
</html>
运行以上代码,我们会发现控制台打印信息如下图所示:

从图中可以看出,type 就是标签名,其他字段比较常用的有 key、ref 以及 props,其中 props 中会包含 className、style 和 children 等字段。这些信息最终会映射成真实的 DOM 结点,所以这就是我们熟知的 Virtual DOM,而 ReactDOM.render() 函数就是将虚拟 DOM 转换成真实 DOM 的工具。
我们现在可以得出一个结论,React.createElement() 负责根据代码生成虚拟 DOM,ReactDOM.render() 负责将虚拟 DOM 映射到真实 DOM 上。
究竟 React.createElement() 和 ReactDOM.render() 是如何将程序转换成真实 DOM 的呢?接下来,我们就来试着实现 React.createElement() 和 ReactDOM.render() 的逻辑,模拟一下这个过程。
先来实现 React.createElement() 方法:
const React = {
// 创建DOM描述对象 即虚拟DOM
createElement(tag, attrs, ...children) {
let vnode = {
type: tag,
props: {
...attrs,
children,
}
};
return vnode;
}
};
以上代码会生成下面的虚拟 DOM 结构:

然后是 ReactDOM.render() 方法:
const ReactDOM = {
// 渲染真实DOM
render(vnode, container) {
let realDOM = this.generateDOM(vnode);
container.appendChild(realDOM);
},
// 获取真实DOM
generateDOM(vnode) {
let elem = document.createElement(vnode.type);
// 特殊key值映射
let specialKeyMap = {
className: 'class',
fontSize: 'font-size',
};
let {props} = vnode;
// 设置DOM属性
props && Object.keys(props).forEach(key => {
if (key === 'children') {
// 处理子结点
props.children.forEach(child => {
if (typeof child === 'string') {
// 纯内容结点
elem.appendChild(document.createTextNode(child));
} else {
// DOM结点
elem.appendChild(this.generateDOM(child));
}
});
} else if (key === 'style') {
// 设置样式属性
let styleObj = props.style;
let styleItems = [];
Object.keys(styleObj).forEach(styleKey => {
styleItems.push(`${specialKeyMap[styleKey] || styleKey}:${styleObj[styleKey]}`);
});
elem.setAttribute('style', styleItems.join(';'));
} else {
// 设置其他属性
elem.setAttribute(specialKeyMap[key] || key, props[key]);
}
});
return elem;
}
};
最后我们把前面引用的React库替换成上面我们自己实现的代码,然后运行,见证奇迹的时刻到了:

只需两段简短的代码,我们就生成了一个迷你版的虚拟 DOM,并最终生成了真实的 DOM 结构,是不是很简单?当然,React 所实现的功能远不止这些,我们后续会继续介绍。
React: JSX生成真实DOM结点的更多相关文章
- React系列文章:JSX生成真实DOM结点
在上一篇文章中,我们介绍了Babel是如何将JSX代码编译成可执行代码的,随后也实现了一个自己的解析器,模拟了Babel编译的过程. 现在我们再来回顾一下,假定有如下业务代码: const style ...
- React系列文章:无状态组件生成真实DOM结点
在上一篇文章中,我们总结并模拟了JSX生成真实DOM结点的过程,今天接着来介绍一下无状态组件的生成过程. 先以下面一段简单的代码举例: const Greeting = function ({name ...
- React: 无状态组件生成真实DOM结点
在上一篇文章中,我们总结并模拟了 JSX 生成真实 DOM 结点的过程,今天接着来介绍一下无状态组件的生成过程. 先以下面一段简单的代码举例: const Greeting = function ({ ...
- React: 有状态组件生成真实DOM结点
上次我们分析了无状态组件生成 DOM 的过程,无状态组件其实就是纯函数,它不维护内部的状态,只是根据外部输入,输出一份视图数据.而今天我们介绍的有状态组件,它有内部的状态,因此在组件的内部,可以自行对 ...
- Vue视图渲染原理解析,从构建VNode到生成真实节点树
前言 在 Vue 核心中除了响应式原理外,视图渲染也是重中之重.我们都知道每次更新数据,都会走视图渲染的逻辑,而这当中牵扯的逻辑也是十分繁琐. 本文主要解析的是初始化视图渲染流程,你将会了解到从挂载组 ...
- React入门-JSX和虚拟dom
1.JSX理解 举例: const element = <h1>Hello, world!</h1>; 这被称为 JSX,是一个 JavaScript 的语法扩展.建议在 Re ...
- [react] 什么是虚拟dom?虚拟dom比操作原生dom要快吗?虚拟dom是如何转变成真实dom并渲染到页面的?
壹 ❀ 引 虚拟DOM(Virtual DOM)在前端领域也算是老生常谈的话题了,若你了解过vue或者react一定避不开这个话题,因此虚拟DOM也算是面试中常问的一个点,那么通过本文,你将了解到如下 ...
- 从 0 到 1 实现 React 系列 —— 1.JSX 和 Virtual DOM
看源码一个痛处是会陷进理不顺主干的困局中,本系列文章在实现一个 (x)react 的同时理顺 React 框架的主干内容(JSX/虚拟DOM/组件/生命周期/diff算法/setState/ref/. ...
- React系列文章:Babel编译JSX生成代码
上次我们总结了React代码构建后的Webpack模块组织关系,今天来介绍一下Babel编译JSX生成目标代码的一些规则,并且模拟整个生成的过程. 我们还是拿最简单的代码举例: import {gre ...
随机推荐
- Ubuntu下卸载anaconda
转载:https://blog.csdn.net/m0_37407756/article/details/77968724(一)删除整个anaconda目录: 由于Anaconda的安装文件都包含在一 ...
- 分布式ID生成总结
1.数据库自增id 新建一个公共库,库里面新建一个序列表,主键id自增,每次请求增加数据都往这个表中插入数据,然后获取到id,然后使用即可. 优点:方便简单 缺点:单库生成自增id,高并发下,会有瓶颈 ...
- script的src和img的src跨域的区别
原理上都是利用标签的src可绕过同源限制,跨域请求的特点, 硬要说不同,那么区别在于:img只能单向发送get请求,不可访问响应内容(只是展现),而script可对其进行解析
- spring boot使用AOP统一处理web请求
为了保证服务的高可用,及时发现问题,迅速解决问题,为应用添加log是必不可少的. 但是随着项目的增大,方法增多,每个方法加单独加日志处理会有很多冗余 那在SpringBoot项目中如何统一的处理Web ...
- Difference between java.lang.RuntimeException and java.lang.Exception
In Java, there are two types of exceptions: checked exceptions and un-checked exceptions. A checked ...
- Kubernetes 及安装注意事项
Docker Desktop for Mac/Windows开启Kubernetes 及安装注意事项 Table of Contents 1 解决方案 2 注意事项 2.1 Choose Kubeco ...
- linux 使用 Python 画图工具matplotlib 提示display 错误
import matplotlib.pyplot as plt Traceback (most recent call last): File "<stdin>", l ...
- 设计模式 AOP,OOP
AOP.OOP在字面上虽然非常类似,但却是面向不同领域的两种设计思想. 简单说,AOP面向动词领域,OOP面向名词领域 AOP: (Aspect Oriented Programming) 面向切面编 ...
- lambd
匿名函数 1.匿名函数格式 void test01() { []() { cout << "hello world" << endl; }(); } 2使用 ...
- Spring Boot与mybatis整合
完整的项目截图 一:pom依赖 新增ojdbc6及batis-spring-boot-starter依赖 <dependency> <groupId>com.oracle< ...