React之虚拟DOM到真实DOM经历了什么

栏目: 服务器 · 发布时间: 6年前

  • 通过 create-react-app easy_react 脚手架创建一个项目
  • 清理 src 目录,这里我们只留下一个 index.js 即可
  • 修改 index.js 如下
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
const style = {
  color: 'red',
  background: 'yellow',
  fontSize:'20px'
}
ReactDOM.render(
  <h1 id={'gu_yan'} className={'gu-yan'} style={style}>
    Guyan
  </h1>, document.getElementById('root'));
复制代码

启动项目

  • 执行 yarn start 页面效果如下
    React之虚拟DOM到真实DOM经历了什么

分析阶段

  • index.js 的代码拷贝到 babel 中进行转换,效果如下
    React之虚拟DOM到真实DOM经历了什么
  • 分析上图
    • 比较代码前后的变化,我们发现变化如下
    /**
     * <h1 id={'gu_yan'} className={'gu-yan'} style={style}>
     *  Guyan
     * </h1>, document.getElementById('root'));
     */
                ‖
                ‖ 
                ‖
                ↓
    React.createElement("h1", {
      id: 'gu_yan',
      className: 'gu-yan',
      style: style
    }, "Guyan")
    复制代码
  • index.js 文件,修改如下,发现页面无变化,控制台打印结果如下图
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
const style = {
  color: 'red',
  background: 'yellow',
  fontSize: '20px'
};
const element = React.createElement("h1", {
  id: 'gu_yan',
  className: 'gu-yan',
  style: style
}, "Guyan")
console.log(element)
ReactDOM.render(element, document.getElementById('root'));
复制代码
React之虚拟DOM到真实DOM经历了什么
  • 如上图可知 React.createElement() 执行返回的结果是一个对象,这个对象我们就称之为虚拟 DOM ,到这里我们就可以总结出虚拟 DOM 的由来了
    • 【1】在 webpack 打包的时候会调用 babel-loader ,将 JSX 语法转义为 React.createElement(...) 的形式
    • 【2】 React.createElement 执行返回一个对象,这对象就是虚拟 DOM
  • 解析 React.createElement 的执行过程
    • 【1】收集属性对象,处理特殊的属性比如 childrenkeyref ;本文只举例说明 children
      • 【1-1】创建一个 props 对象
      • 【1-2】将传进来的第二个参数对象中的每一个属性都挂在 props 上,值为第二个参数对象中相对于的值
      • 【1-3】给 props 挂一个 children 属性,值为传进来的第三个参数
        • 注: children 这个值是一个字符串或者是一个数组。如果执行 React.createElement 的时候传入的参数大于3,那么 children 的值就是一个数组,其值为除前两个之外的所有属性
    • 【2】将传入的 type 和收集的属性对象作为参数传入到 ReactElement 中执行( reactElement(type,props) )
    • 【3】 ReactElement 执行创建一个 newElement 对象
    • 【4】给 newElement 挂一个 $$typeof 属性,这里我们统一将其值赋为 Symbol(react.element)
    • 【5】给 newElement 挂一个 type 属性,值为传进来的 type
    • 【6】给 newElement 挂一个 props 属性,值为传进来的 props
    • 【7】返回 newElement (虚拟 DOM
  • 解析 ReactDom.render 的执行过程
    • 【1】判断传入的虚拟 DOM 的类型
      • 【1.1】普通文本类型,则第一个参数可能是 string 或者 number ,则直接创建一个真实文本节点
      • 【1.2】普通 html 标签组件,比如 <div></div> ,第一个参数对象的 type 属性是一个 string 类型,创建一个真实的 DOM 节点并将第一个参数的 props 属性中的一些普通属性挂载到这个真实的 DOM 节点上,并对一些特殊的属性比如 children 的进行处理,详细见下文的实现阶段
      • 【1.3】函数组件( function Component ),第一个参数对象的 type 属性是一个 function 类型, type 执行传入第一个参数的 props
      • 【1.4】类组件( class Component ),第一个参数对象的 type 属性上有一个 isReactComponent 属性, new type(props) 创建实例,并让实例的 render 方法执行
    • 【2】将创建的真实节点插入到父节点中

实现阶段

react.js

// _react.js

    class Component {
        static isReactComponent = true;
        constructor(props){
            this.props = props;
        }
    }
    
    function ReactElement(type,props){
        const virtual_dom = {};
        virtual_dom.$$typeof = Symbol.for('react.element');
        virtual_dom.type = type;
        virtual_dom.props = props;
        return virtual_dom;
    }

    function createElement(type,config,children){
        const props = {};
        for (const propName in config){
            if (config.hasOwnProperty(propName)){
                props[propName] = config[propName];
            }
        }
        const childrenLength = arguments.length - 2;
        if (childrenLength === 1){
            props.children = children;
        }else if (childrenLength > 2){
            props.children = Array.from(arguments).slice(2);
        }
        return ReactElement(type,props);
    }
    
    export default {createElement,Component}
复制代码

react-dom.js

// _react_dom.js
    
    function render(virtual_dom,parent_node){
        if (typeof virtual_dom ==='string' || typeof virtual_dom === 'number'){
        /**
         *处理直接渲染文本节点的情况,比如ReactDOM.render('guYan', document.getElementById('root'));
         */
         return parent_node.appendChild(document.createTextNode(virtual_dom));
        }
        
        if (virtual_dom.type.isReactComponent){
         /**
         *处理直接渲染Class Component的情况
         */
            virtual_dom = new virtual_dom.type(virtual_dom.props).render();
            render(virtual_dom,parent_node);
        }
        
        if (typeof virtual_dom.type === 'function'){
         /**
         *处理直接渲染function Component的情况
         */
            virtual_dom = virtual_dom.type(virtual_dom.props);
            render(virtual_dom,parent_node);
        }
        let {type , props} = virtual_dom;
        let real_dom = document.createElement(type); // 根据type创建真实节点
        for (const propName in props){ // 对props进行处理
            if(props.hasOwnProperty(propName)){
                const prop =  props[propName];
                if(propName === 'className'){
                    real_dom.className = prop;
                }else if (propName === 'style'){
                    let cssText = Object.keys(prop).map(attr=>(`${attr.replace(/([A-Z])/g,function(){
                        return `-${arguments[1].toLowerCase()}`
                    })}:${prop[attr]}`)).join(';');
                    real_dom.style.cssText = cssText;
                }else if (propName === 'children'){ 
                    /**
                     *children可能是字符串或者是数组,如果是字符串转化成数组,然后递归调用render,需要注意的是
                     * 此时传入的父节点是我们创建的real_dom
                     */
                    let childArr = Array.isArray(prop) ? prop : [prop];
                    childArr.forEach(child=>render(child,real_dom));
                }else {
                    real_dom.setAttribute(propName,prop);
                }
            }
        }
        return parent_node.appendChild(real_dom);
    }
复制代码

写在最后

  • 日常推荐使用函数组件
    • 1.使用简单
    • 2.节约内存
    • 3.提高性能
  • 本文中的仅仅在表面层说明原理。并未深度剖析,如何错误还望指教。谢谢!

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Machine Learning

Machine Learning

Kevin Murphy / The MIT Press / 2012-9-18 / USD 90.00

Today's Web-enabled deluge of electronic data calls for automated methods of data analysis. Machine learning provides these, developing methods that can automatically detect patterns in data and then ......一起来看看 《Machine Learning》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

URL 编码/解码
URL 编码/解码

URL 编码/解码