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 ...
随机推荐
- PAT-2019年冬季考试-甲级 7-4 Cartesian Tree (30分)(最小堆的中序遍历求层序遍历,递归建树bfs层序)
7-4 Cartesian Tree (30分) A Cartesian tree is a binary tree constructed from a sequence of distinct ...
- 硬盘分区及Linux文件系统
1. 硬盘物理结构 硬盘物理上主要分为: 盘片 磁道 扇区 机械臂 磁头 主轴 磁道: 当硬盘盘片旋转时,磁头若固定在一个位置上,则磁头会在盘片表面划出一个圆形轨迹,这些圆形轨迹就叫做磁道.以盘片中心 ...
- ETF计算公式:IOPV
IOPV=(申购.赎回清单中必须现金替代的替代金额+申购.赎回清单中退补现金替代成份证券的数量与最新成交价相乘之和+申购.赎回清单中可以现金替代成份证券的数量与最新成交价相乘之和+申购.赎回清单中禁止 ...
- Appium之Toast元素识别
问题思考 在日常使用App过程中,经常会看到App界面有一些弹窗提示(如下图所示)这些提示元素出现后等待3秒左右就会自动消失,那么我们该如何获取这些元素文字内容呢? Toast简介 Android中的 ...
- Centos7安装文件传输软件rz sz
一直使用Xshell的xftp传输文件,谁知道忽然无法正常使用. 于是,决定用户rz进行传输 安装步骤也比较简单 1.首先安装第三方源(以下源比默认源包含更多安装包,建议添加该源使用) yum ins ...
- vue使用px2rem
配置 flexible 安装 lib-flexible 在命令行中运行如下安装: 1 npm i lib-flexible --save 引入 lib-flexible 在项目入口文件 main.js ...
- DockerFile语法【h】
DockerFile在我理解就是可以将所需要构建镜像的功能.组件都天前配置好,然后直接生成一个Image,而不是先生成镜像,再通过修改容器的方法来生成最终需要的镜像. 镜像的定值实际上就是定值每一 ...
- .NET Core开源Quartz.Net作业调度框架实战演练
一.需求背景 人生苦短,我用.NET Core!作为一枚后端.NET开发人员,项目实践常遇到定时Job任务的工作,在Windows平台最容易想到的的思路Windows Service服务应用程序,而在 ...
- ThinkPHP3开发模式,控制器操作,配置文件,框架语法
ThinkPHP的开发模式 tp框架有两种使用模式:开发模式(调试模式),一种是生产模式(运行模式) define('APP_DEBUG', true); //调试模式 define('APP_DE ...
- linux安装docker,并在docker上运行springboot项目
docker架构示例图 仓库---> 镜像 ---> 容器 一.安装docker 1.通过 uname -r 命令查看你当前的内核版本 uname -r 2使用 root 权限登录 Ce ...