React-模拟jsx渲染原理

先看一个初始化jsx的例子
import React from 'react';
import ReactDOM from 'react-dom';
let obj = {color: 'red'};
let ary=[
    {id:1,title:'中序遍历的算法原理'},
    {id:2,title:'二叉树算法原理及代码实现'}
];

ReactDOM.render(<div id="box"
                     className="box"
                     style={obj}>
    <h2>javascript和数据结构</h2>
    <ul>
        {
            ary.map((item,index)=>{
                return <li key={index}>{item.title}</li>;
            })
        }
    </ul>
</div>, window.root);
复制代码

我们可以看到这样的结果:

jsx语法在这里不做赘述,那么浏览器在jsx语法到真实DOM之间都做了哪些事呢?

  1. 首先webpack(webpack底层有babel,babel帮我们在es5/6和react做了一个preset)先把JS中所有的JSX元素变为CREATE-ELEMENT模式,我们可以通过在线的babel转译看一下:

这里并没有完全展示,可以看代码,我复制过来:

React.createElement(
    "div",
    { id: "box",
        className: "box",
        style: styleObj },
    React.createElement(
        "h2",
        { className: "title" },
        "KK\u662F\u4E2A\u6F02\u4EAE\u59D1\u5A18"
    ),
    React.createElement(
        "ul",
        { className: "newsItem" },
        React.createElement(
            "li",
            { key: "1" },
            "\u54C8\u54C8\u54C8"
        ),
        React.createElement(
            "li",
            { key: "2" },
            "\u5475\u5475\u5475"
        ),
        React.createElement("li", { key: "3" })
    )
);
复制代码

经过Babel转义的语法在浏览器中会解析出什么呢?我们定义一个变量等于以上代码,看一下结果,是一个对象:

ReactDom.render便是将此对象渲染为真实的DOM,在页面中展示出来,这个过程就是jsx的渲染原理。

所以我们要做的就是模拟createElement和render,代码如下:

class React {
    static createElement(type, options, ...arg) {
        //=>处理初始值
        //=>ARG存储的是剩下的参数集合:即使没有其余的参数了,ARG也是一个空数组
        //=>OPTIONS肯定是一个对象(不管传或者没传)
        options = options || {};

        //=>HANDL
        let obj = {
            type,//->type:type
            key: null,
            ref: null,
            props: {}
        };

        //=>KEY && REF
        ['key', 'ref'].forEach(item => {
            if (item in options) {
                obj[item] = options[item];
                delete options[item];
            }
        });

        //=>PROPS
        obj.props = {...options};
        let len = arg.length;
        switch (len) {
            case 0:
                //=>没有子节点不需要设置CHILDREN
                break;
            case 1:
                //=>只有一个子节点,CHILDERN不是一个数组
                obj.props.children = arg[0];
                break;
            default:
                //=>拥有多个子节点(把深度克隆后的数组赋值给他)
                obj.props.children = JSON.parse(JSON.stringify(arg));
        }

        return obj;
    }
}

class ReactDOM {
    static handChild(children, newEle) {
        if (typeof children === 'object') {
            //->当前唯一的子节点是一个新元素(需要重新调用RENDER创建一个新的元素,把元素放在newEle中)
            ReactDOM.render(children, newEle);
        } else {
            //->当前唯一的子节点是一个文本
            newEle.appendChild(document.createTextNode(children));
        }
    }

    static render(objJSX, container, callback) {
        let {type, props} = objJSX,
            newEle = document.createElement(type);
        //=>给先创建的元素设置各种属性
        for (let key in props) {
            if (!props.hasOwnProperty(key)) continue;

            //=>CHILDREN
            if (key === 'children') {
                let children = props['children'];
                if (children instanceof Array) {
                    //=>有多个子节点(遍历一个个来)
                    children.forEach(itemChild => {
                        ReactDOM.handChild(itemChild, newEle);
                    });
                    continue;
                }
                //=>只有一个子节点
                ReactDOM.handChild(children, newEle);
                continue;
            }

            //=>CLASS
            if (key === 'className') {
                newEle.setAttribute('class', props['className']);
                continue;
            }

            //=>STYLE 把PROPS中STYLE的值依次遍历,并且设置给元素
            if (key === 'style') {
                for (let key in props['style']) {
                    if (props['style'].hasOwnProperty(key)) {
                        newEle['style'][key] = props['style'][key];
                    }
                }
                continue;
            }

            //...

            //->一般情况下把PROPS中的属性直接赋值给元素即可(例如:props={id:'box'}  => newEle.setAttribute('id':'box') ...)
            newEle.setAttribute(key, props[key]);
        }

        container.appendChild(newEle);
        callback && callback();
    }
}

let styleObj = {color: 'red'};
ReactDOM.render(<div id="box"
                     className="box"
                     style={styleObj}>
    <h2 className="title">javascript和数据结构</h2>
    <ul className="newsItem">
        <li key='1'>中序遍历的算法原理</li>
        <li key='2'>二叉树算法原理及代码实现</li>
        <li key='3'></li>
    </ul>
</div>, window.root);

复制代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值