Today, I will mainly bring you an implementation of managing JSX and virtual DOM. React was born with the birth of JSX, which was implemented by Babel.

What does JSX ultimately translate into?

We can go to Babel’s website and try it out and see what it translates into?

You can see that the JSX we usually write is actually translated into the code on the right. This explains why ReactDOM needs React.

From this we can see that react. createElement has three important parameters

/** tag dom attrs attribute... Childrens child element */React.createElement(tag,attrs,... childrens) {return { tag, attrs, childrens } 
}
Copy the code

With that in mind, we also need to know how React renders. This time to our ReactDOM to explain.

Do you remember use ReactDom. Render (vnode, document getElementById (” root “)) this method?

So what does reactdom.render do for us? The following code unofficial code for the idea of the code, and streamlined components. Render receives two arguments, one for vnode and the other for rendering vNode into the container, which explains why you need a <> to be included when writing JSX.

ReactDOM.render(vnode,container){
    if(vnode===undefined) return;
    
    if(typeof node === 'string') {const textNode document.createTextNode(vnode); 
        return container.appendChild(textNode); 
    }
    
    // If none of the above is true, it is our createElemenet object
    const {tag,attrs} = vnode; 
    const dom = document.createElement(tag); 
    if(attrs){ Object.keys(attrs).forEach(key={ 
        const value attrs[key] 
        // Set the properties
        setAttribute(dom,key,value) 
        }) 
    }
    
    // Render the child node
    vnode.childrens.forEach(child= >render(child,dom));
    return container.appendChild(dom);
}
Copy the code
// The operation to set the property
function setAttribute(dom,key,value){ 
    if(key==='className'){ key = 'class' } 
    if(/on\w+/.test(key)){ 
        key = key.toLowerCase(); 
        dom[key] = value || ' '; 
    }
    else if(key === 'style') {if(! value ||typeof value==='string'){ 
            dom.style.cssText = value || ' '; 
        }
        else if(value && typeof= = ='object') {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

As you can see from the above code, render will determine the type for vNode when rendering, if it is a string, it will return directly.

If it was a VNode object, we would create our tag using document.createElement, and the created BDOM element would do some attrs setting. I think you all know this line of thinking.

Componentized thinking.

There are two componentization implementations in React: a class component and a function component. For simplicity, the implementation of functions will eventually lead to the implementation of classes.


function renderComponent(comp){ 
    const renderer = comp.render(); 
    comp.base = _render(renderer) 
}

class Component { 
    // As for life cycle execution, you can figure out how to implement the call.
    constructor(props={}){ 
        this.props = props; this.state = {}; 
    } 
    
    setState(stateChange){ 
        Object.assign(this.state,stateChange); 
            renderComponent(this); }}export default Component;
Copy the code

If a function or typeof Object.prototype. render exists when we render a VNode, we will instantiate the class.

if(typeof vnode.tag === 'function') { 
    const comp = createComponent(vnode.tag,vnode.attrs);   
    setComponentProps(comp,vnode,attrs); 
    return comp.base; 
}

function renderComponent(comp){ 
    const renderer = comp.render(); 
    comp.base = render(renderer) 
}

function createComponent(comp,props){ 
    let inst; if(comp.prototype && comp.prototype.render )
        { inst = new comp(props) }
        else{ // If it is a function component
        inst = new Component(props); 
        inst.constructor = comp; 
        inst.render = function(){ 
            return this}}return inst; 
}
Copy the code

Something like that, but of course the official implementation of React doesn’t work that way, so I’m oversimplifying. Managed by the same class component.

A simple implementation of diff

Diff’s main idea is to compare and update.

With this in mind, the easiest way to do this is to use the BDOM to compare your VNODE, and if you do updates, use JS to update the values in your BDOM. But if you have too much time on your tree, you’re going to have too much time complexity. That’s the idea. Of course, the actual React diff algorithm is more complicated.

We need to pay attention to the following points when using it.

Whenever the root element has a different type, React will tear down the old tree and build the new one from scratch.

When the tree is removed, the old Dom node is destroyed. The component instance receives WillUnmout(). Build a new tree with DidMount(). Any state associated with the old tree will be lost.

Minimize changes to the root element.

Do not use index for key. Performance may deteriorate during sorting

Because React relies on heuristics, performance suffers if the assumptions behind them are not met.

  1. The algorithm does not attempt to match subtrees of different component types. If you see yourself alternating between two component types with very similar output, you might want to set them to the same type. In practice, we have not found this to be a problem.

  2. Bonds should be stable, predictable, and unique. Unstable keys (such as those generated by ‘math.random ()’) cause many component instances and DOM nodes to be recreated unnecessarily, resulting in performance degradation and loss of child component state.