react源码(一):实现createElement和render函数

由于react依赖JSX语法,因此最开始须要对JSX的执行过程作大体介绍。至于JSX的相关基础知识,请参照JSX官网css

JSX的执行过程

  • 1.写出代码
<h1>Hello</h1>复制代码
  • 2.用打包工具如webpack,调用babel-loader把JSX语法转换成JavaScript函数,createElement
  • 3.运行代码时,浏览器执行createElement,获得虚拟DOM,也就是react元素。其实react元素就是一个纯粹的JavaScript对象,描述了会在页面上显示的DOM的属性。
  • 4.把虚拟DOM(react元素)给render函数,render就会把虚拟DOM转换成真实的DOM,并插入到页面中去。

react中的虚拟DOM

上面说道react元素就是虚拟DOM,接着来看一下react元素有哪些属性html

// 首先是调用React.createElement生成react元素,并在控制台输出let element = React.createElement("h1", {className: 'title',style: {color: 'red'}
}, React.createElement('span', null, 'hello'), ' world');console.log(JSON.stringify(element,null,2));

ReactDOM.render(
    element, document.getElementById('root')
);// 下面展现的属性并非彻底的react元素的属性,为了简化,把一些当前阶段用不到的属性进行删除/**
{
     "type": "h1",
     "props": {
         "className": "title",
         "style": {
             "color": "red"
         },
         "children": [
         	{
                 "type": "span",
                 "props": {
                     "children": "hello"
                 }
             },
             " world"
         ]
     }s
 }
 */复制代码

主要思路

  • 1.createElement函数的主要任务就是建立一个JavaScript对象
  • 2.render函数的主要任务就是建立文本节点和各类元素,将其插入到页面中去

代码实现

  • 1.createElement函数
/**
 * 
 * @param {*} type 元素类型
 * @param {*} config 配置对象,通常来讲就是属性对象
 * @param {*} children 第一个儿子
 */function createElement(type,config,children){if(config){delete config._owner;delete config._store;
    }let props = {...config};if(arguments.length > 3){
        children = Array.prototype.slice.call(arguments,2);
    }// children多是数组(多于一个儿子),也多是一个字符串或数字、也多是一个null 或 react元素props.children = children;return {
        type,props
    }
}复制代码
  • 2.render函数
/**
 * 虚拟DOM转换成真实DOM 并插入到容器
 * @param {*} vdom 虚拟DOM
 * @param {*} container 插入到哪一个容器
 */function render(vdom,container){const dom = createDOM(vdom);
    container.appendChild(dom);
}/**
 * 把虚拟DOM变成真实DOM
 * @param {} vdom null 数字 字符串 React元素 数组 暂时不支持数组
 */function createDOM(vdom){// null就什么都不作// 若是vdom是一个字符串或者数字的话,建立一个文本的DOM节点返回if(typeof vdom  === 'string' || typeof  vdom ===  'number'){return document.createTextNode(vdom);
    }// 若是不存在返回空串 undefined nullif(!vdom){return '';
    }// 不然就是一个react元素let {type,props} = vdom;let dom = document.createElement(type); //span divupdateProps(dom,props); // 把虚拟DOM上的属性设置到真实DOM上// 处理子节点 若是子节点就是一个单节点 而且是字符串或数字if (typeof props.children === 'string' || typeof props.children === 'number') {
        dom.textContent = props.children; // dom.textContent = 'hello';// 说明是一个单React元素节点} else if (typeof props.children === 'object' && props.children.type) {
        render(props.children,dom);// 若是儿子是一个数组,说明有多个子节点}else if(Array.isArray(props.children)){
        reconcileChildren(props.children,dom);
    }else{ // 若是出现了其余的意外状况dom.textContent = props.children ? props.children.toString():'';
    }return dom;
}/**
 * 把子节点从虚拟DOM所有转换成真实DOM 并插入到父节点中去
 * @param {*} childrenVdom 子节点的虚拟DOM数组
 * @param {*} parentDOM 父节点真实DOM
 */function reconcileChildren(childrenVdom,parentDOM){
    childrenVdom.forEach(childVdom => {
        render(childVdom,parentDOM);
    });
}/**
 * 把属性对象中的属性设置到DOM元素上
 * @param {*} dom 
 * @param {*} props 
 */function updateProps(dom,props){for(let key in props){if(key === 'children') continue;if(key === 'style'){let styleObj = props[key];for(let key in styleObj){
                dom.style[key] = styleObj[key]; // dom.style.color = 'red';}
        }else{
            dom[key] = props[key]; // dom.className = 'title';}
    }
}复制代码
相关文章
相关标签/搜索