[React]虚拟DOM

1.虚拟DOM(virtual DOM)

我们在render()方法中书写的JSX代码,既有js代码又有html代码,但是实际上浏览器不能识别JSX。

let element = (<div className="red">
  <span>hehe</span>
</div>)

需要通过babel-loader将其转化为js代码。实际上就是转为React中的createElement()方法调用。

var element = React.createElement("div", {
  className: "red"
}, React.createElement("span", null, "hehe"));

该方法返回一个对象,这个对象的内容和DOM类似,但是不是真实的DOM,所以称之为虚拟DOM。

{
    "type": "div",
    "props": {
        "className": "red",
        "children": {
            "type": "span",
            "props": {
                "children": "hehe"
            }
        }
    }
}

最后浏览器展示的DOM是通过ReactDOM.render()方法处理后,成为真实DOM。

2.createElement()方法模拟实现

function createElement(type, config, children) {
    console.log(children)
    const props = {};
    for (let propName in config) {
        props[propName] = config[propName];
    }
    const childrenLength = arguments.length - 2;
    if (childrenLength === 1) {
        props.children = children; // 为元素,文本,数字
    } else if (childrenLength > 1) { // 为数组
        props.children = Array.from(arguments).slice(2);
    }
    return { type, props}
}

3.render方法模拟实现

function render(element, container) {
    if (typeof element === 'string' || typeof element === 'number') { // 文本或者数字
       container.appendChild(document.createTextNode(element));
       return;
    }
    let type = element.type;
    let props = element.props;

    if (typeof type === 'function') { // 函数组件或者类组件
        if (type.isComponent) { // 类
            element = (new type(props)).render();
        } else {
            element = type(props);
        }
        type = element.type;
        props = element.props;
    }
    let domEle = document.createElement(type);
    for (let propName in props) {
        if (propName === 'className') {
            domEle.className = props[propName];
        } else if(propName === 'style') {
            const styleObj = props[propName];
            for(let styleProp in styleObj) {
                domEle.style[styleProp] = styleObj[styleProp];
            }
        } else if(/^on[A-Z]/.test(propName)) {
            domEle[propName.toLowerCase()] = props[propName];
        } else if(propName === 'children') {
            const children = Array.isArray(props[propName]) ? props[propName] : [props.children];
            children.forEach(child => render(child, domEle));
        }
    }
    container.appendChild(domEle);
}