• React原理(一)VirtualDom 到渲染UI


    VirtualDom介绍

    virtualDom实际就是个object类型的字典{} ,字典中包含typepropschildrentextContentkey来描述的是dom元素的。

    如下所示真是的dom

    React
    • 1
    • 2

    描述dom的VirtualDom,则为

     {type: "div",props: { className: "container" },children: [{type: "div",props: null,children: [{type: "text",props: {textContent: "React"}}]}]
     } 
    
    • 1
    • 2

    VirtualDom生成过程

    我们可以先看babel在线转换

    {funcDoc}
    function fucDoc() {return
    function component
    }
    • 1
    • 2
    • 3
    • 4
    • 5

    可以看到上述代码经过babel转换后会调用React.createElement api

    "use strict";
    
    /*#__PURE__*/
    React.createElement("div", {name: "33"
    }, /*#__PURE__*/React.createElement("div", null, funcDoc));
    
    function fucDoc() {return /*#__PURE__*/React.createElement("div", null, "function component");
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    而调用React.createElement 的api后返回的刚好是我们想要的VirtualDOm 如下代码所示返回的刚好是VirtualDOm对象。

    /**
     *
     * @param type 标签类型
     * @param props 标签的属性
     * @param children子元素
     * @returns {{children: *[], type, props}}
     */
    export default function createElement (type, props, ...children) { const childElements = ...省略处理逻辑...return {type,props,childElements}
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    实现tinyReact

    环境配置

    统一配置

    我们需要告诉babel使用我们自己写的TinyReact,需要配置babel

    在.babelrc文件中将React改为TinyReact

    {"presets": ["@babel/preset-env",["@babel/preset-react",{"pragma": "TinyReact.createElement" //React改为TinyReact}]]
    } 
    
    • 1
    • 2

    通过以上配置告诉babel调用TinyReact.createElement来转换jsx文件

    单个文件配置

    除了统一配置外也可以在单个文件中添加 /** @jsx TinyReact.createElement */注释,来实现单个文件配置

    生成VirtualDom

    上一步操作会让babel调用我们自己写的createElement方法

    //index.js文件

    const virtualDOM = (

    我是标题一

    我是标题二

    div嵌套一下
    里面的div
    {shouldHide &&
    条件渲染1
    }{!shouldHide &&
    隐藏我
    }2, 3
    ) console.log(virtualDOM)
    • 1
    • 2
    • 3

    如果createElement直接返回打印结果如下

    可以看到文本被放在children中直接返回了,显然不对的,另外条件渲染!shouldHide为false被直接返回了,我们想要的是隐藏标签的效果

    文本节点对应virtualDom为

    {type: 'text',props: {textContent: '我是文本'}
    } 
    
    • 1
    • 2
    /**
     *
     * @param type 标签类型
     * @param props 标签的属性
     * @param children子元素
     * @returns {{children: *[], type, props}}
     */
    export default function createElement (type, props, ...children) {const childElements = [].concat(...children).reduce((result,child) => {if (child !== false && child !== true && child !== null) {if (child instanceof Object) {result.push(child)} else {// 文本节点 手动调用 createElementresult.push(createElement("text", {textContent: child}))}} return result}, [])return {type,props: Object.assign({ children: childElements }, props),children: childElements}
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    实现render方法渲染UI

    //index.js

    ......
    TinyReact.render(, root) 
    
    • 1
    • 2

    //TinyReact/index.js

    import createElement from "./createElement";
    import render from "./render";
    
    export default {render,createElement
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    //TinyReact/render.js

    import mountElement from "./mountElement";
    export default function render(virtualDOM, container, oldDom) {mountElement(virtualDOM, container)
    } 
    
    • 1
    • 2
    • 3

    //TinyReact/mountElement

    import {isFunction} from "./utils";
    import mountComponent from "./mountComponent";
    import mountNativeElement from "./mountNativeElement";
    
    export default function mountElement(virtualDOM, container) {if (isFunction(virtualDOM)) {// 判断是否为组件,组件的逻辑稍后实现 // mountComponent(virtualDOM, container, oldDOM)} else {mountNativeElement(virtualDOM, container, oldDOM)}
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    //TinyReact/mountNativeElement

    import createDOMElement from "./createDOMElement"
    
    export default function mountNativeElement(virtualDOM, container) {const newElement = createDOMElement(virtualDOM)container.appendChild(newElement)
    } 
    
    • 1
    • 2
    • 3
    • 4

    //TinyReact/createDOMElement

    export default function updateElementNode(element, virtualDOM) {const newProps = virtualDOM.propsObject.keys(newProps).forEach(propName => {const newPropsValue = newProps[propName]if (propName.slice(0, 2) === "on") {const eventName = propName.toLowerCase().slice(2)element.addEventListener(eventName, newPropsValue)} else if (propName === "value" || propName === "checked") {element[propName] = newPropsValue} else if (propName !== "children") {if (propName === "className") {element.setAttribute("class", newPropsValue)} else {element.setAttribute(propName, newPropsValue)}}})
    } 
    
    • 1
    • 2
  • 相关阅读:
    C#中的循环
    HTML5 Canvas绘制图形从入门到精通
    从「博客园」的困境,到「用爱发电」~
    HoloLens 2上使用Azure空间定位点
    Java 修改文件时间不生效以及解决办法
    MySQL建库指定字符集编码
    刷题11.27
    Debezium报错处理系列之六十八:No resolvable bootstrap urls given in bootstrap.servers
    【无标题】CondaHTTPError: HTTP 000 CONNECTION FAILED for url
    Dart(11)-mixin
  • 原文地址:https://blog.csdn.net/qq_53225741/article/details/126370750