zoukankan      html  css  js  c++  java
  • 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 所实现的功能远不止这些,我们后续会继续介绍。

  • 相关阅读:
    谈一谈对象池SafeObjectPool能干什么
    .net core 程序退出事件
    .NETCore 快速开发做一个简易商城
    Git创建子分支,合并分支并提交
    Vue项目中关闭eslint的方法
    Missing space before function parentheses
    单引号变双引号 格式化去掉分号
    Docker Swarm搭建多服务器下Docker集群
    Asp.Net Core中服务的生命周期选项区别和用法
    KnockoutJS-模板绑定
  • 原文地址:https://www.cnblogs.com/liuhe688/p/10970776.html
Copyright © 2011-2022 走看看