在上一篇文章中,我们介绍了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就是标签名,其他字段比较常用的有keyref以及props,其中props中会包含classNamestylechildren等字段。这些信息最终会映射成真实的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: 无状态组件生成真实DOM结点

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

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

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

  3. React: JSX生成真实DOM结点

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

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

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

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

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

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

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

  7. React系列文章:Webpack模块组织关系

    现代前端开发离不开打包工具,以Webpack为代表的打包工具已经成为日常开发必备之利器,拿React技术栈为例,我们ES6形式的源代码,需要经过Webpack和Babel处理,才能生成发布版文件,在浏 ...

  8. 学习React系列(六)——更新dom细节于原理

    React更新dom的依据: 1.不同类型的elements会产生不同的树 2.通过render方法中包含key属性的子元素,开发者可以示意哪些子元素可能是稳定的. 更新过程: 一.根元素类型不同:旧 ...

  9. React 系列文章(1): npm 手动搭建React 运行实例 (新手必看)

    摘 要 刚接触React 开发, 在摸索中构建react 运行环境,总会遇到各种坑:本文,将用最短时间解决webpack+react 环境搭建问题. 1.如果你还没有React基础 看这里. 2.如果 ...

随机推荐

  1. js 将图片连接转换称base64格式

    我们把图像文件的内容直接写在了HTML 文件中,这样做的好处是,节省了一个HTTP 请求.坏处呢,就是浏览器不会缓存这种图像.现在我们提供一个js: function convertImgToBase ...

  2. [学习笔记]Java的public,protected,private,缺省的作用域

    0.引言 Java的访问指示符public,protected,private,缺省可以用来修饰类和方法. 1.作用域如下 具体如下: 作用域       当前类    同一package   子孙类 ...

  3. css之margin

    参考地址:http://www.imooc.com/learn/680 标准盒模型 元素尺寸 可视尺寸-clientWidth(标准)——就是上图中的border box包含的尺寸. 占据尺寸-out ...

  4. 红包外挂史及AccessibilityService分析与防御

    最近在做一个有趣的外挂的小玩意,前提我们要了解一个重要的类AccessibilityService 转载请注明出处:https://lizhaoxuan.github.io 前言 提起Accessib ...

  5. 单元测试-代码覆盖率 EclEmma

    1. EclEmma的介绍 EclEmma是一个开源的软件测试工具,可以在编码过程中查看代码调用情况.也可以检测单覆盖率. 2. Eclipse下EclEmma安装 1. 选择Help->Ecl ...

  6. hdu4638 莫队算法

    莫队算法基础题,题目看懂就能做出来 #include<iostream> #include<cstring> #include<cstdio> #include&l ...

  7. python 全栈开发,Day33(tcp协议和udp协议,互联网协议与osi模型,socket概念,套接字(socket)初使用)

    先来回顾一下昨天的内容 网络编程开发架构 B/S C/S架构网卡 mac地址网段 ip地址 : 表示了一台电脑在网络中的位置 子网掩码 : ip和子网掩码按位与得到网段 网关ip : 内置在路由器中的 ...

  8. python 全栈开发,Day30(纸牌游戏,异常和错误,异常处理)

    一.纸牌游戏                                                                                              ...

  9. 主机可以ping通虚拟机,但是虚拟机ping不通主机的方法(转)

    https://blog.csdn.net/hskw444273663/article/details/81301470

  10. 《剑指offer》-二叉搜索树与双向链表

    输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向. 题目的描述不是很习惯.题目的意思是把二叉树从左到右遍历,相当于双向链表的遍历. 其实 ...