React的VDOM簡(jiǎn)單實(shí)現(xiàn)

jsx是通過(guò)babel轉(zhuǎn)換成react.CreateElement()方法的調(diào)用

轉(zhuǎn)換前

import React from 'react';

function App() {
  return <h1>Hello World</h1>;
}

轉(zhuǎn)換后

import React from 'react';

function App() {
  return React.createElement('h1', null, 'Hello world');
}

React 17 在 React 的 package 中引入了兩個(gè)新入口,這些入口只會(huì)被 Babel 和 TypeScript 等編譯器使用。

新的 JSX 轉(zhuǎn)換不會(huì)將 JSX 轉(zhuǎn)換為 React.createElement,而是自動(dòng)從 React 的 package 中引入新的入口函數(shù)并調(diào)用

Babel 的 v7.9.0 及以上版本可支持全新的 JSX 轉(zhuǎn)換。

function App() {
  return <h1>Hello World</h1>;
}

轉(zhuǎn)換后

// 由編譯器引入(禁止自己引入?。?import {jsx as _jsx} from 'react/jsx-runtime';

function App() {
  return _jsx('h1', { children: 'Hello world' });
}

參數(shù)表現(xiàn)不一樣,但最后都是生成ReactElement,用js對(duì)象表示dom結(jié)構(gòu)。

react的主要元素類(lèi)型

null,文本,元素標(biāo)簽,函數(shù)組件和類(lèi)組件。

示例jsx

function FnComp() {
  return <div>Hello FnComp</div>
}

class ClassComp extends React.Component {
  render() {
    return <div>Hello ClassComp</div>
  }
}

const virtualDOM =
  <div>
    {null}
    react
    <h1>h1標(biāo)簽</h1>
    <FnComp></FnComp>
    <ClassComp></ClassComp>
  </div>
轉(zhuǎn)換后
function FnComp() {
    return React.createElement("div", null, "Hello FnComp");
}

class ClassComp extends React.Component {
    render() {
        return React.createElement("div", null, "Hello ClassComp");
    }

}

const virtualDOM = React.createElement("div", null,
null,
"react", 
React.createElement("h1", null, "h1\u6807\u7B7E"), 
React.createElement(FnComp, null), 
React.createElement(ClassComp, null));

實(shí)現(xiàn)createElement

function createElement(type, props, ...children) {
    let newChildren = []
    children.forEach(child => {
        if (child === null || child === false || child === true) {
            return
        }
        // 處理過(guò)的子節(jié)點(diǎn)
        if (typeof child === 'object') {
            newChildren.push(child)
        }
        //文本節(jié)點(diǎn)
        if (typeof child === 'string') {
            newChildren.push({
                type: 'text',
                props: { textContent: child },
            })
        }
    })

    return {
        type,
        props: Object.assign({ children: newChildren }, props),
    }
}

實(shí)現(xiàn)render

function render(VDOM, container) {
    let element = mountElement(VDOM)
    container.appendChild(element)
}

function mountElement(VDOM) {
    let element = null
    if (typeof VDOM.type === 'function') {
        // class組件有render方法
        if (VDOM.type.prototype.render) {
            element = mountClassElement(VDOM)
        } else {
            element = mountFnElement(VDOM)
        }
    } else {
        element = mountNativeElement(VDOM)
    }
    return element
}

function mountNativeElement(VDOM) {
    let element = null
    if (VDOM.type === 'text') {
        element = document.createTextNode(VDOM.props.textContent)
    } else {
        element = document.createElement(VDOM.type)
        // 設(shè)置屬性
        setAttr(element, VDOM)
        VDOM.props.children && VDOM.props.children.forEach(child => {
            element.appendChild(mountElement(child))
        })
    }
    return element
}

function mountFnElement(VDOM) {
    let element = VDOM.type()
    return mountElement(element)
}

function mountClassElement(VDOM) {
    let element = new VDOM.type()
    return mountElement(element.render())
}

function setAttr(element, VDOM) {
    VDOM.props && Object.keys(VDOM.props).forEach((propName) => {
        let value = VDOM.props[propName]
        if (propName.startsWith('on')) {
            let eventName = propName.substring(2)
            element.addEventListener(eventName, value)
        } else if (propName === 'className') {
            element.setAttribute('class', value)
            //children屬性不需要在標(biāo)簽上顯示
        } else if (propName !== "children") {
            element.setAttribute(propName, value)
        }
    })
}

Component先定義個(gè)空類(lèi)就行

class Component {

}

測(cè)試代碼

要借助babel編譯jsx后給react調(diào)用

import myReact from "./myReact"

function FnComp() {
  return <div>Hello FnComp</div>
}

class ClassComp extends myReact.Component {
  render() {
    return <div>Hello ClassComp</div>
  }
}

const virtualDOM =
  <div>
    {null}
    react
    <h1 className='myClass' extraProp='extraProp' onclick={() => { console.log(1111); }}>h1標(biāo)簽</h1>
    <FnComp></FnComp>
    <ClassComp></ClassComp>
  </div>

myReact.render(virtualDOM, document.getElementById("root"))
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容