从零开始实现一个react-02-JSX和虚拟dom

140 阅读2分钟

1.React文件:

  • title会被babel转译成虚拟dom
//html中引入的index.js文件

import React from "./react";       // ①
import ReactDom from "./react-dom" // ②
const title = (
    <h2 className = 'title'>
        <span> hello React</span>
    </h2> );
// 上⾯代码会被babel转义成下⾯对象 ⽽这个对象就是所谓的虚拟DOM
/*
var title = React.createElement("h2", {
 className: "title"
}, " hello React ");
*/
console.log(title);
  • 接着来看下引入的React:
// react/index.js

const React  = {
    createElement
}
function createElement(tag,attrs,...children) {
    return{
        tag,
        attrs,
        children
    }
}
export default React

编译后,打开浏览器,在localhost:1234端口可见:

2.ReactDom的render方法:

1.字符串:

index.js文件中修改如下:

import React from "./react";
import ReactDom from "./react-dom";
const title = (
    <h2 className = 'title'>
        <span> hello React</span>
    </h2> );
// 上⾯代码会被babel转义成下⾯对象 ⽽这个对象就是所谓的虚拟DOM
/*
var title = React.createElement("h2", {
 className: "title"
}, " hello React ");
*/
console.log(title);
// 测试render方法为字符串的时候是否可以渲染
ReactDom.render("你好世界",document.querySelector("#root"))

然后来看下react-dom的代码:

  • 和上面写React类似,这里面也有个render方法,需要2个参数
    • vDom:虚拟dom
    • container:节点
// react-dom/index.js

const ReactDom = {
    render
}
function render(vDom,container){
    if (vDom==="undefined") return ;
    if(typeof vDom === "string"){
        const text = document.createTextNode(vDom);
         return container.appendChild(text);
    }
}
export default ReactDom;

测试结果:

2.虚拟dom对象:

ReactDomrender方法中,主要还得是对于虚拟dom对象的处理,宗旨就是先处理一层,再递归操作~!!! X3 (重要的事情说3遍)

  • const {tag,attrs} = vDom;解构出对应的值。
  • 遍历所有的attrs,通过setAttributes方法设置属性
  • setAttrubutes方法中将attrs中的属性转换为dom属性。
  • 然后去看vDom的子节点children,然后递归调用
const ReactDom = {
    render
}
function render(vDom,container){
    //如果vDom未定义或者是字符串
    if (vDom==="undefined") return ;
    if(typeof vDom === "string"){
        const text = document.createTextNode(vDom);
         return container.appendChild(text);
    }
  
    //虚拟dom对象
    const {tag,attrs} = vDom;
    const dom = document.createElement(tag);
    if(attrs){
        Object.keys(attrs).forEach(key=>{
            const value = attrs[key]
            setAttribute(dom,key,value)
        })
    }
    //递归渲染子节点
    vDom.children.forEach(child=>{
        render(child,dom)
    })
    return container.appendChild(dom);
}
// 设置属性
function setAttribute(dom,key,value){
    //将属性名className转换成class
    if(key === "className"){
        key = "class"
    }
    // 如果是事件 onClick onBlur
    if(/on\w+/.test(key)){
        // 转小写
        key = key.toLowerCase();
        dom.key = value || " "
    }else if(key ==="style") //style的value可以是值,也可以是个对象
    {
        if(!value || typeof value === "string")
        {
            dom.style.cssText = value || "";

        }else if(value && typeof  value === "object")
        {
            // {width:20} 是value,key是width
            for(let k in value)
            {
                if(typeof value[k] ==="number")
                {
                    dom.style[k] = value[k] +"px";
                }else
                {
                    dom.style[k] = value[k];
                }
            }
        }
    }else
    {    //其他属性
        if(key in dom)
        {
            dom[key] = value || "";
        }
        if(value)
        {
            dom.setAttribute(key,value)
        }
        else
        {
            dom.removeAttribute(key)
        }
    }
}
export default ReactDom;

结果如下: