# 简介

# 概念

虚拟DOM是浏览器DOM与JS对象的形成的映射关系,由JS对象来描述一个DOM的所有信息,使用函数将对象渲染为浏览器真实DOM,正是由于VDOM的特性,使得DOM脱离了浏览器端,衍生了react-native、weex、ionic等前端组件化跨平台应用,用来使用vue,react,angular前端框架开发拥有原生性能体验的原生跨平台APP。甚至是近年来流行的跨多种平台的打包工具(Taro,chameleon)等。

# createElement

一段HTML代码会被编译为一个对象,在React中俗称JSX语法,即HTML in JS ,我们来看看Babel是将一段HTML代码编译为JS对象的吧

<div class="test">
  <span>text2</span>
  <span>text2</span>
  <span>text2</span>
  <span>text2</span>
</div>

编译后:

"use strict";
    React.createElement("div", {
    class: "test"
    }, 
    React.createElement("span", null, "text2"),
    React.createElement("span", null, "text2"),
    React.createElement("span", null, "text2"),
    React.createElement("span", null, "text2")
);

# 动手实现createElement

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="./VDom.js"></script>
</head>
<body>
    <div id='app'></div>
</body>
<script>
//创建DOM节点
    let el = createElement('div', {
        class: "test",
        style: 'color:red'
    }, [
        ['div', {
            class: 'child1'
        }, "子节点1"],
        ['div', {
                class: 'child2'
            },
            [
                ['li', {
                    class: 'li1'
                }, "li1"],
                ['li', {
                    class: 'li2'
                }, 'li2']
            ]
        ]
    ])
    //挂载
    window.app.appendChild(el);
</script>
</html>

VDOM.js

function createElement(elType, attrs, children) {//对应参数:(DOM类型,节点属性,子节点数组或者字符串)
    let el;
    //新增节点并赋值节点类型
    typeof elType==="string"?el=document.createElement(elType): new Error("is not a String");
    for (const key in  attrs) {//便利所有属性,为当前节点赋值
        if (attrs.hasOwnProperty(key)) {
            const element = attrs[key];
            el.setAttribute(key,element);//键值对的形式
        }
    }
    if(typeof children==="string"){
        el.textContent=children;//是字符串直接赋予textContent属性
    }else{
        children.forEach(element => {//否则数组进行递归调用该方法深层渲染
           let subEl= createElement(...element);
           el.appendChild(subEl);//添加到父元素中
        });
    }
    return el;//返回当前元素
}

# React中的虚拟DOM

function App(){
    return <div>123</div>
}
//等价于下方
React.createElement('div',{name:'CJ',age:23},['123'])
//所以每次当我们调用指定的类或者函数返回的虚拟DOM时,babel会将他转换为createElement的形式去执行
//渲染一个函数或者组件我们只要使用<App></App>即可
React.render(<App/>,'#app');
//也等价于
React.render({App(arg1,arg2)},'#app');
//转换后:
React.render(React.createElement('div',{name:'CJ',age:23},['123']),'#app');


//使用class的组件
class App extends React.Component{
    constructor(props){
        super(props);
        console.log(props.name)
    }
    render(){
        return{
            <div>123</div>
        }
    }
}
React.render(<App/>,'#app');
//也等价于
React.render({new App({name:'cj'})},'#app');
//转换后:
React.render(React.createElement('div',{name:'CJ',age:23},['123']),'#app');