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: 无状态组件生成真实DOM结点
在上一篇文章中,我们总结并模拟了 JSX 生成真实 DOM 结点的过程,今天接着来介绍一下无状态组件的生成过程. 先以下面一段简单的代码举例: const Greeting = function ({ ...
- React: 有状态组件生成真实DOM结点
上次我们分析了无状态组件生成 DOM 的过程,无状态组件其实就是纯函数,它不维护内部的状态,只是根据外部输入,输出一份视图数据.而今天我们介绍的有状态组件,它有内部的状态,因此在组件的内部,可以自行对 ...
- React: JSX生成真实DOM结点
在上一篇文章中,我们介绍了 Babel 是如何将 JSX 代码编译成可执行代码的,随后也实现了一个自己的解析器,模拟了 Babel 编译的过程. 现在我们再来回顾一下,假定有如下业务代码: const ...
- React系列文章:无状态组件生成真实DOM结点
在上一篇文章中,我们总结并模拟了JSX生成真实DOM结点的过程,今天接着来介绍一下无状态组件的生成过程. 先以下面一段简单的代码举例: const Greeting = function ({name ...
- 从 0 到 1 实现 React 系列 —— 1.JSX 和 Virtual DOM
看源码一个痛处是会陷进理不顺主干的困局中,本系列文章在实现一个 (x)react 的同时理顺 React 框架的主干内容(JSX/虚拟DOM/组件/生命周期/diff算法/setState/ref/. ...
- React系列文章:Babel编译JSX生成代码
上次我们总结了React代码构建后的Webpack模块组织关系,今天来介绍一下Babel编译JSX生成目标代码的一些规则,并且模拟整个生成的过程. 我们还是拿最简单的代码举例: import {gre ...
- React系列文章:Webpack模块组织关系
现代前端开发离不开打包工具,以Webpack为代表的打包工具已经成为日常开发必备之利器,拿React技术栈为例,我们ES6形式的源代码,需要经过Webpack和Babel处理,才能生成发布版文件,在浏 ...
- 学习React系列(六)——更新dom细节于原理
React更新dom的依据: 1.不同类型的elements会产生不同的树 2.通过render方法中包含key属性的子元素,开发者可以示意哪些子元素可能是稳定的. 更新过程: 一.根元素类型不同:旧 ...
- React 系列文章(1): npm 手动搭建React 运行实例 (新手必看)
摘 要 刚接触React 开发, 在摸索中构建react 运行环境,总会遇到各种坑:本文,将用最短时间解决webpack+react 环境搭建问题. 1.如果你还没有React基础 看这里. 2.如果 ...
随机推荐
- java.sql.SQLException: ORA-28040: 没有匹配的验证协议(12c或者12c rac)
1.plsql可以连接,java程序不能连接,报如下错误: 一直以来用的都是服务器上的Oracle数据库,今天改成连接本地Oracle 12c数据库是出问题了.hibernate连接Oracle12c ...
- vi与vim
vi 的使用 基本上 vi 共分为三种模式,分别是『一般模式』.『编辑模式』与『指令列命令模式』. 这三种模式的作用分别是: 一般模式:以 vi 打开一个档案就直接进入一般模式了(这是默认的模式).在 ...
- 转载:详解Java 自动装箱与拆箱的实现原理
原文:http://www.jb51.net/article/111847.htm 什么是自动装箱和拆箱 自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对 ...
- Expm 2_2 查找中项问题
对于长度为n的整型数组A,随机生成其数组元素值,然后实现一个线性时间的算法,在该数组中查找其中项. package org.xiu68.exp.exp3; import java.util.Array ...
- Android动画分类
动画分类 View动画(补间动画).帧动画.属性动画 View动画(补间动画)包括:平移.旋转.缩放.透明度,View动画是一种渐近式动画 帧动画:图片切换动画 属性动画:通过动态改变对象的属性达到动 ...
- 求阶乘的和(for循环)
第二种方法:
- easyUI基础入门
头部需要引人文件:<!DOCTYPE html><html><head> <meta charset="utf-8"> <ti ...
- JQuery插件jqModal应用详解(十二)
JqModal 是jQuery的一个插件,用来在web浏览器中显示自定义通告,而且它为通用窗口框架奠定了基础. 1. 多模型支持 2. 支持拖拽和重定义大小 3, 支持远程加载窗口内容(ajax和if ...
- 手动部署 kubernetes 1.9 记录
前言 目前 kubernetes 正式版本已经到1.10版本.因为前面有大佬(漠然)已经采完坑,所以自己也试着部署 kubernetes 1.9 体验下该版本的新特性.对于前面部署的 kubernet ...
- 用HTML+CSS画出一个同心圆
参加web前端校招的同学们经常会遇到这样的面试题:用HTML+CSS画出一个同心圆. 例如: 这道题主要考验的是基础盒模型布局能力和倒圆角属性的巧用. 1.html代码 <body> &l ...