在上一篇文章中,我们介绍了 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结点的更多相关文章

  1. React系列文章:JSX生成真实DOM结点

    在上一篇文章中,我们介绍了Babel是如何将JSX代码编译成可执行代码的,随后也实现了一个自己的解析器,模拟了Babel编译的过程. 现在我们再来回顾一下,假定有如下业务代码: const style ...

  2. React系列文章:无状态组件生成真实DOM结点

    在上一篇文章中,我们总结并模拟了JSX生成真实DOM结点的过程,今天接着来介绍一下无状态组件的生成过程. 先以下面一段简单的代码举例: const Greeting = function ({name ...

  3. React: 无状态组件生成真实DOM结点

    在上一篇文章中,我们总结并模拟了 JSX 生成真实 DOM 结点的过程,今天接着来介绍一下无状态组件的生成过程. 先以下面一段简单的代码举例: const Greeting = function ({ ...

  4. React: 有状态组件生成真实DOM结点

    上次我们分析了无状态组件生成 DOM 的过程,无状态组件其实就是纯函数,它不维护内部的状态,只是根据外部输入,输出一份视图数据.而今天我们介绍的有状态组件,它有内部的状态,因此在组件的内部,可以自行对 ...

  5. Vue视图渲染原理解析,从构建VNode到生成真实节点树

    前言 在 Vue 核心中除了响应式原理外,视图渲染也是重中之重.我们都知道每次更新数据,都会走视图渲染的逻辑,而这当中牵扯的逻辑也是十分繁琐. 本文主要解析的是初始化视图渲染流程,你将会了解到从挂载组 ...

  6. React入门-JSX和虚拟dom

    1.JSX理解 举例: const element = <h1>Hello, world!</h1>; 这被称为 JSX,是一个 JavaScript 的语法扩展.建议在 Re ...

  7. [react] 什么是虚拟dom?虚拟dom比操作原生dom要快吗?虚拟dom是如何转变成真实dom并渲染到页面的?

    壹 ❀ 引 虚拟DOM(Virtual DOM)在前端领域也算是老生常谈的话题了,若你了解过vue或者react一定避不开这个话题,因此虚拟DOM也算是面试中常问的一个点,那么通过本文,你将了解到如下 ...

  8. 从 0 到 1 实现 React 系列 —— 1.JSX 和 Virtual DOM

    看源码一个痛处是会陷进理不顺主干的困局中,本系列文章在实现一个 (x)react 的同时理顺 React 框架的主干内容(JSX/虚拟DOM/组件/生命周期/diff算法/setState/ref/. ...

  9. React系列文章:Babel编译JSX生成代码

    上次我们总结了React代码构建后的Webpack模块组织关系,今天来介绍一下Babel编译JSX生成目标代码的一些规则,并且模拟整个生成的过程. 我们还是拿最简单的代码举例: import {gre ...

随机推荐

  1. Python3基础 print %xX 十六进制大小写

             Python : 3.7.3          OS : Ubuntu 18.04.2 LTS         IDE : pycharm-community-2019.1.3    ...

  2. java中为什么notify()可能会导致死锁,而notifyAll()则不会

    简单的说,notify()只唤醒一个正在等待的线程,当该线程执行完以后施放该对象的锁,而没有再次执行notify()方法,则其它正在等待的线程 则一直处于等待状态,不会被唤醒而进入该对象的锁的竞争池, ...

  3. 深入理解JVM虚拟机

    JVM平台上还可以运行其他语言,运行的是Class字节码.只要能翻译成Class的语言就OK了.挺强大的. JVM厂商很多 垃圾收集器.收集算法 JVM检测工具 关于类的加载: Java代码中,类型( ...

  4. 关于怎么提取m3u8地址

    摘自: https://blog.51cto.com/4373601/1920758 很长时间没有写博客了,这一段时间比较忙,接下来的日子要坚持写博客了,后期抽空会把这一年多的测试心得补上来,写博客其 ...

  5. matlab学习笔记11_3高维数组操作 filp, shiftdim, size, permute, ipermute

    一起来学matlab-matlab学习笔记11 11_3 高维数组处理和运算 filp, shiftdim, size, permute, ipermute 觉得有用的话,欢迎一起讨论相互学习~Fol ...

  6. awk(gawk)文本报告生成器

    awk是gawk的链接文件,是一种优良的文本处理工具,实现格式化文本输出,是Linux和Unix现有环境中功能最强大的数据处理引擎之一.这种编程及数据操作语言的最大功能取决于一个人拥有的知识量,使用& ...

  7. 【NPDP笔记】第一章 新产品开发战略

    1.1 战略很重要 1.2 战略定义 使命/愿景/核心价值观:成为领导者 公司/经营战略:市场份额扩大10% 创新战略:强调技术,外部合作 职能战略:IT战略,人力资源战略 1.3明确组织方向 组织身 ...

  8. 20190726_安装CentOS7minimal版本后需要做的优化和配置

    20190726_安装CentOS7minimal版本后需要做的优化和配置 CentOS系统镜像下载地址:https://www.centos.org/ CentOS的Minimal(最小化安装版本) ...

  9. (CSDN迁移)JAVA多线程实现-继承Thread

    继承Thread方法: extends Thread 重写覆盖run()方法: @Override public void run() 通过start()方法启动线程. threadDemo01.st ...

  10. 【剑指offer】链表中的倒数第k个结点

    输入一个链表,输出该链表中倒数第k个结点. 分析: 定义两个结点p1和p2都指向头节点,p1先走k-1步,然后p1和p2一起走,当p1走到链表尾部时,p2指向的结点就是倒数第k个结点 遍历一遍链表即可 ...