This is the 12th day of my participation in Gwen Challenge

Let’s write a paragraph about JSX, which we won’t go into too much here, and then we’ll talk about JSX syntax

const ele = (
    const ele = (
	<div className="box" size="25">
    hello <span>zidea</span>
    </div>))Copy the code

Then use Babel to convert the above JSX to JS as follows

"use strict";

const ele = /*#__PURE__*/React.createElement("div", {
  className: "box".size: "25"
}, "hello "./*#__PURE__*/React.createElement("span".null."zidea"));
Copy the code

Take a quick look at the generated code and analyze it briefly

React.createElement(tag,attrs,children)
Copy the code
  • Tag is the name of the tag to be created
  • Attrs organizes attribute objects in the form of objects that hold attribute names and values in key-value pairs
  • Children are children

Implement the createElement method

  • Create React object
const React = {
    createElement
}
Copy the code
  • Implement the creatElement method

// createElement
function createElement(tag,attrs,... children){
    return {
        tag,
        attrs,
        children
    }
}

export default React;

Copy the code
  • Introduce the React object and use the React createElement method
import React from './react'

const ele = (
    <div className='title' title='react source analysis'>
        hello <span>react</span>
    </div>
)

console.log(ele)
Copy the code

Implement rendering

We all know how to render a virtual node to a page by calling the Render method of ReactDom in React and passing in the virtual node and adding the container DOM element to the virtual node.

ReactDOM.render(ele,document.querySelector("#root"))
Copy the code
import React from './react'

const ele = (
    <div className='box' title='hello zidea'>
        hello <span>react</span>
    </div>
)

// console.log(ele)

ReactDOM.render(ele,document.querySelector("#root"))
Copy the code

The next step is to create a folder called react-dom and create an index.js file in that folder to implement the Render method of ReactDOM

const ReactDOM = {
    render
}

function render(vnode,container){
    //TODO
}

export default ReactDOM;
Copy the code

I won’t explain too much about a common JS module and exposing the ReactDOM object. Let’s focus on the render method implementation.

Text node implementation

function render(vnode,container){
    //TODO
    if(vnode === undefined ) return;
    // vnode is equal string
    if(typeof vnode === 'string') {//create textNode
        const textNode = document.createTextNode(vnode)
        return container.appendChild(textNode)
    }
}
Copy the code
  • The react argument is no different, okay
    • Vnode Virtual node
    • The container vessel
  • It is preferred to check whether the VNode is empty or return if the vNode is empty
  • Then determine if vNode is a string, and if it is a string, add a text node
ReactDOM.render('react'.document.querySelector("#root"))
Copy the code

Virtual node

ReactDOM.render(ele,document.querySelector("#root"));
Copy the code
console.log(vnode)
Copy the code
function render(vnode,container){...// deconstruct vnode
    const {tag} = vnode;

    //create dom object
    const dom = document.createElement(tag)

    container.appendChild(dom)
}
Copy the code
  • Deconstruct the VNode object
  • Create dom objects based on the tag
  • Add the DOM object to the container

Retrieve attributes

function render(vnode,container){...// deconstruct vnode
    const {tag,attrs} = vnode;

    //create dom object
    const dom = document.createElement(tag)

    if(attrs){
        // property key: className box
        Object.keys(attrs).forEach(key= >{
            const val = attrs[key]
        })
    }
    ...
}
Copy the code
  • Deconstruct attrs from virtual nodes (VNodes)
  • Attrs is then iterated to get the attribute value

Set properties

function setAttribute(dom,key,value){}Copy the code

Implement the setAttribute method

function setAttribute(dom,key,value){

    // convert className to class
    // 1. event 2.class 3.style etc 

    // class case
    if(key === 'className'){
        key = 'class'
    }

    // event case
    if(/on\w+/.test(key)){
        //to lower case
        key = key.toLowerCase();
        dom[key] = value || ' '
    }else if(key === 'style') {if(! value ||typeof value === 'string'){
            dom.style.cssText = value || ' ';
        }else if(value && typeof value === 'object') {//{width:16}
            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)
        }
    }

}
Copy the code

There are many properties in the DOM that you can use to add styles, add events, save data, and so on. We do this by determining the attribute key and value type in attrs

The class attribute

If the key is className, we will change the key to class while adding the value

if(key === 'className'){
    key = 'class'
}
Copy the code
Event attributes
if(/on\w+/.test(key)){
    //to lower case
    key = key.toLowerCase();
    dom[key] = value || ' '
}
Copy the code
Style properties
else if(key === 'style') {if(! value ||typeof value === 'string'){
        dom.style.cssText = value || ' ';
    }else if(value && typeof value === 'object') {//{width:16}
        for(let k in value){
            if(typeof value[k] === 'number'){
                dom.style[k] = value[k] + 'px'
            }else{
                dom.style[k] = value[k]
            }
        }
    }
}
Copy the code

The style case is a little more complicated than the other attributes and can be handled in two ways: a string or an object

  • In the case of strings, the assignment is straightforward
  • Object is

To start working with objects, let’s take a look at the following JSX objects compiled as javascript objects by default

const ele = (
	<div className="box" size="25" style={{width:16}}>
    hello <span>zidea</span>
    </div>
)
Copy the code
.style: {
    width: 16}...Copy the code

Here an object is nested within the Style object, and we parse the object to get the name and style value of each style. The style value can be a numeric value or a string.

for(let k in value){
    if(typeof value[k] === 'number'){
        dom.style[k] = value[k] + 'px'
    }else{
        dom.style[k] = value[k]
    }
}
Copy the code
if(attrs){
    // property key: className box
    Object.keys(attrs).forEach(key= >{
        const val = attrs[key]
        setAttribute(dom,key,val)
    })
}

container.appendChild(dom)
Copy the code

Render child node

The child nodes are rendered recursively

function render(vnode,container){... vnode.children.forEach(child= >render(child,dom))

    container.appendChild(dom)
}

Copy the code