Vitual Dom

  1. concept

DOM information and structure are represented by JavaScript objects, which are re-rendered when the state changes. This JavaScript object is called the Virtual DOM.

  1. why

DOM manipulation is slow, and even minor manipulation can cause pages to be reformatted, which can be very costly. Js objects are faster and simpler to process than DOM objects. The diff algorithm is used to compare the differences between the old and new VDOM, so that DOM operations can be performed in batches and minimized, thus improving performance.

3. Use it in React

React uses JSX syntax to describe views. Babel-loader translates JSX to react. CreateElement (…). This function will generate a VDOM to describe the real DOM. In the future, if the state changes, VDOM will make corresponding changes, and then diff algorithm will be used to compare the difference between the old and new VDOM to make the final DOM operation.

JSX form:

class HelloMessage extends React.Component {
  render() {
    return (
    <div key="0">
      <div className={hello} key="1" >
        Hello {this.props.name}
      </div>
    </div>
    );
  }
}

ReactDOM.render(
  <HelloMessage name="Taylor" />.document.getElementById('hello-example'));Copy the code

Babel-loader: pass in the tag type, props, constants, variables, nested children ~ respectively

class HelloMessage extends React.Component {
  render() {
    return React.createElement(
      "div",
      { key: "0" },
      React.createElement(
        "div",
        { className: hello, key: "1" },
        "Hello ".this.props.name
      )
    );
  }
}

ReactDOM.render(React.createElement(HelloMessage, { name: "Taylor" }), document.getElementById('hello-example'));
Copy the code

4. React.createelement () converts the passed element into a virtual DOM

Core API

  1. React.createElement: Creates the virtual DOM

(1) Node types: Text node, HTML tag node, function component, class component, fragment, and other nodes such as portal.

Define various node types in the external index.js file:

// import React from "react";
// import ReactDOM from "react-dom";
import React from "./kreact/";
import ReactDOM from "./kreact/ReactDOM";
import Component from "./kreact/Component";
import "./index.css";

// JSX =>createElement =>render(vnode->node)

function FunctionComponent({name}) {
  return (
    <div className="border function">
      hello, {name}
      <button onClick={()= > console.log("omg")}>click</button>
    </div>
  );
}


class ClassComponent extends Component {
  render() {
    const {name} = this.props;
    return <div className="border function">hello, {name}</div>; }}const jsx = (
  <div className="border">
    <p>This is a text</p>
    <a href="https://kaikeba.com/">Classes begin!</a>
    <div className="border">
      <h5>hello</h5>
    </div>
    <FunctionComponent name="function" />
    <ClassComponent name="class" />
    <>
      <h5>Text 1</h5>
      <h5>Text 2</h5>
    </>

    {[1.2.3].map(item= > {
      return (
        <div className="border" key={item}>
          <p>{item}</p>
          <p>{item}</p>
        </div>
      );
    })}
  </div>
);

/ / element, the container
// vnode->node, update the node rendering to the container
ReactDOM.render(jsx, document.getElementById("root"));

Copy the code

Define createElement inside index.js

function createElement(type, props, ... children) {
  // Delete native
  if (props) {
    delete props.__source;
    delete props.__self;
  }

  return {
    type: type,
    props: {
      ...props,
      / /! The treatment here is slightly different from the source code, where there's only one element, children is an object, or more than one, an array
      children: children.map(child= >
        typeof child === "object" ? child : createTextNode(child)
      )
    }
  };
}

// The p tag TEXT type is handled separately in the spanning tree
function createTextNode(text) {
  return {
    type: "TEXT".props: {
      children: [].nodeValue: text
    }
  };
}
export default {
  createElement
};

Copy the code
  1. React.component: Implements custom components that are inherited by other class components
export default class Component {
  static isReactComponent = {};
  constructor(props) {
    this.props = props; }}Copy the code
  1. Reactdom. render: Render the real DOM, convert the virtual DOM to the real DOM, and mount the real DOM under the Container tag.

Define the render function in reactdom.js.

function render(vnode, container) {
  // console.log("vnode", vnode);
  // Convert the virtual DOM to the real DOM
  const node = createNode(vnode);
  // Update node to container
  container.appendChild(node);
}

// Create a node based on vNode
function createNode(vnode) {
  const {type, props} = vnode;
  let node;
  // Create a node by calling document.createxxx () based on the node type. Function and class types are handled separately
  if (typeof type === "function") {
    node = type.isReactComponent ? 
        updateClassComponent(vnode)
      : updateFunctionComponent(vnode);
  } else if (type === "TEXT") {
    node = document.createTextNode("");
  } else if (type) {
    node = document.createElement(type);
  } else {
    node = document.createDocumentFragment();
  }
  // Add attributes to the node
  updateNode(node, props);
  // The recursion generates children into real nodes
  reconcilerChildren(props.children, node);
  return node;
}

function reconcilerChildren(children, node) {
  for (let i = 0; i < children.length; i++) {
    let child = children[i];
    // Iterate over the create element
    // Read the children[I] type
    if (Array.isArray(child)) {
      for (let j = 0; j < child.length; j++) {
        // Recursively call renderrender(child[j], node); }}else {
      // Recursively call renderrender(children[i], node); }}}// Update the attributes on the node, such as className and nodeValue
function updateNode(node, nextVal) {
  Object.keys(nextVal)
    .filter(k= >k ! = ="children")
    .forEach(k= > {
      if (k.slice(0.2) = = ="on") {
        // Start with on, consider it an event, the source code processing is more complicated,
        let eventName = k.slice(2).toLocaleLowerCase();
        node.addEventListener(eventName, nextVal[k]);
      } else{ node[k] = nextVal[k]; }}); }// function component returns node
function updateFunctionComponent(vnode) {
  const {type, props} = vnode;
  // The function returns the virtual DOM
  const vvnode = type(props);
  const node = createNode(vvnode);
  return node;
}

function updateClassComponent(vnode) {
  const {type, props} = vnode;
  // instantiate class
  const cmp = new type(props); 
  // Call render in class to return the virtual DOM
  const vvnode = cmp.render(); 
  const node = createNode(vvnode);
  return node;
}
export default {
  render
};

Copy the code