(1) We analyze the function of render function and diff function, and analyze the React Component lifecycle mapping and the cycle execution order of parent and child components. In this section, we will continue the content not analyzed in (1). This section describes the processing flow of Preact diff process, vNode to DOM conversion and insertion process. Let us go, continue our source reading journey.

1. Key subfunction analysis

Before analyzing the overall diff process, let’s look at a few key subfunctions that will help us better understand the overall Diff process. We mainly analyze functions outside the DIFF process, so that we can understand the function of subfunctions when we follow the DIFF process.

1.1 props

The Preact props file defines a set of sub-functions for adding, comparing, and event-related properties. Let’s first look at the specific functions it includes.

1.1.1 eventProxy

Let’s first look at the functionality of the event broker. You can see that when we define an options.event hooks, we first execute the options.event hooks to complete the event hooks.

function eventProxy(e) {
    this._listeners[e.type](options.event ? options.event(e) : e);
}
Copy the code

In (1), we also see a lot of options.* hook function, here is the analysis of its function. Options is a basic tool library that allows you to customize a series of functions. Two typical scenarios are as follows:

  • Official use of Preact. First, the function mapping and transformation of Preact to React. For example, compat/ SRC /render. Js defines the Event processing of React. Second, debug/test call, convenient debug, performance testing.
  • Developer custom. Of course, we can define our own options hook. For details, see PreactJS Options.

1.1.2 setStyle

SetStyle, which sets the style attribute of an element. Preact classifies the style attribute into four categories:

  • Elements beginning with ‘-‘. Use setProperty directly to set the style property.
  • Value is a value and the attributes of px need to be set, typically such as height, width, etc. The IS_NON_DIMENSIONAL here contains some properties that don’t require adding px, such as zoom, ine, etc.
  • If value is null, set to “”.
  • By default, style[key] is equal to value.
function setStyle(style, key, value) { if (key[0] === '-') { style.setProperty(key, value); } else if ( typeof value == 'number' && IS_NON_DIMENSIONAL.test(key) === false ) { style[key] = value + 'px'; } else if (value == null) { style[key] = ''; } else { style[key] = value; }}Copy the code

1.1.3 setProperty

The DOM Property function is used to process the Property of a DOM element. The specific processing process is as follows:

  • ClassName Initial processing. For SVG images, className => class, non-SVG images, className => className.
if (isSvg) {
    if (name === 'className') {
        name = 'class'; }}else if (name === 'class') {
    name = 'className';
}
Copy the code
  • Style. There are two types: string/object.
    • String, dom.style.cssText = style;
    • When the object.
      • When oldValue string, dom.style.cssText is empty and oldValue is null.
      • OldValue is an object.
        • OldValue has a property that value does not and is set to “”.
        • Value sets the new style property.
if (name === 'style') { s = dom.style; If (typeof value == 'string') {s.cisstext = value; } else { // oldValue string if (typeof oldValue == 'string') { s.cssText = ''; oldValue = null; } // oldValue object // oldValue has properties but value does not, set to "if (oldValue) {for (let I in oldValue) {if (! (value && i in value)) { setStyle(s, i, ''); If (value) {for (let I in value) {if (! oldValue || value[i] ! == oldValue[i]) { setStyle(s, i, value[i]); } } } } }Copy the code
  • For properties beginning with on, the handling of event. Distinguish onClick and onClickCapture scenarios.
    • If value does not exist, remove the event of name.
    • Value exists, dom adds the Listener for event.
else if (name[0] === 'o' && name[1] === 'n') { useCapture = name ! == (name = name.replace(/Capture$/, '')); nameLower = name.toLowerCase(); name = (nameLower in dom ? nameLower : name).slice(2); if (value) { if (! oldValue) dom.addEventListener(name, eventProxy, useCapture); (dom._listeners || (dom._listeners = {}))[name] = value; } else { dom.removeEventListener(name, eventProxy, useCapture); }}Copy the code
  • Normal operation. Non-svg and name in DOM and name! = list/tagName/form/type/size, setting the dom. The name is equal to the value. Since tagName, form, list, and other properties are read-only properties in the DOM and cannot be set, they are filtered when updated.
else if ( name ! == 'list' && name ! == 'tagName' && name ! == 'form' && name ! == 'type' && name ! == 'size' && ! isSvg && name in dom ) { dom[name] = value == null ? '' : value; }Copy the code
  • In addition to the above and value non-function, name is not equal to “dangerouslySetInnerHTML” processing.
    • www.w3.org/1999/xlink attribute processing, when value is null/false, the attribute corresponding to removeAttributeNS; Otherwise, setAttributeNS ‘corresponding attribute.
    • When value is null, value is false, and the aria-Attributes attribute node is not present, removeAttribute the attribute.
      • There are scenarios where the ARIA attribute is false, so you need to filter attributes of that type.
    • In addition to the above, set setAttribute(name, value) directly.
else if (typeof value ! = 'function' && name ! == 'dangerouslySetInnerHTML') { if (name ! == (name = name.replace(/^xlink:? /, ''))) { if (value == null || value === false) { dom.removeAttributeNS( 'http://www.w3.org/1999/xlink', name.toLowerCase() ); } else { dom.setAttributeNS( 'http://www.w3.org/1999/xlink', name.toLowerCase(), value ); } } else if ( value == null || (value === false && ! /^ar/.test(name)) ) { dom.removeAttribute(name); } else { dom.setAttribute(name, value); }}}Copy the code

1.1.4 diffProps

After analyzing the basic subfunctions, let’s take a look at the core functions of the diffProps function, which is basically props diff for a VNode node and apply changes to the corresponding DOM node.

  • Except for children/key, oldProps have properties but newProps do not, and the setProperty call is set to null.
  • To recursively handle the properties of newProps, setProperty I is updated to the new newProps[I] if the following conditions are met:
    • In addition to the children/value/key/checked
    • In non-hydrate mode, or newProps[I] under hydrate is a function
    • The oldProps and newProps properties do not want to wait.
export function diffProps(dom, newProps, oldProps, isSvg, hydrate) { let i; (I in oldProps) {// set null if (I! == 'children' && i ! == 'key' && ! (i in newProps)) { setProperty(dom, i, null, oldProps[i], isSvg); }} / / processing new props for (I in newProps) {/ / in addition to the children/value/key/checked attribute / / not hydrate model, NewProps [I] = newProps[I] = oldProps[I]! == newProps[i] if ( (! hydrate || typeof newProps[i] == 'function') && i ! == 'children' && i ! == 'key' && i ! == 'value' && i ! == 'checked' && oldProps[i] ! == newProps[I]) {// Set newProps[I] setProperty(dom, I, newProps[I], oldProps[I], isSvg); }}}Copy the code

1.2 diffchildren

DiffChildren contains the core diffChildren functions. Here we will focus on toChildArray functions.

1.2.1 toChildArray

ToChildArray function is mainly to convert children into an array, and do toChildArray recursive processing of the child elements of children.

export function toChildArray(children) {
    if (children == null || typeof children == 'boolean') {
        return [];
    } else if (Array.isArray(children)) {
        return EMPTY_ARR.concat.apply([], children.map(toChildArray));
    }
    return [children];
}
Copy the code

1.3 applyRef/unmount to/removeNode

1.3.1 applyRef

Fires/updates the REF object, executes ref(value) if it is a function, otherwise sets ref.current = value.

export function applyRef(ref, value, vnode) {
  try {
    if (typeof ref == 'function') ref(value);
    else ref.current = value;
  } catch(e) { options._catchError(e, vnode); }}Copy the code

1.3.2 unmount to

Unmount is the unmount function of the node. SkipRemove identifies whether the parent node is separated from the DOM.

  • Clear the current node/component refs.
  • If vNode. type is not a component, skipRemove is reset.
  • Set vnode.dom/ vnode._nextdom to undefined.
  • If the current node is a Component, run componentWillUnmount and set R.ase = R._parentdom = null.
  • Process vnode._children recursively, uninstalling the children of the current element.
  • Remove the current DOM Element when the DOM exists.
export function unmount(vnode, parentVNode, skipRemove) {
  let r;
  if (options.unmount) options.unmount(vnode);
  if ((r = vnode.ref)) {
    if(! r.current || r.current === vnode._dom) applyRef(r,null, parentVNode);
  }
  let dom;
  if(! skipRemove &&typeofvnode.type ! ='function') { skipRemove = (dom = vnode._dom) ! =null;
  }

  vnode._dom = vnode._nextDom = undefined;
  if((r = vnode._component) ! =null) {
    if (r.componentWillUnmount) {
      try {
        r.componentWillUnmount();
      } catch (e) {
        options._catchError(e, parentVNode);
      }
    }
    r.base = r._parentDom = null;
  }
  if ((r = vnode._children)) {
    for (let i = 0; i < r.length; i++) {
      if(r[i]) unmount(r[i], parentVNode, skipRemove); }}if(dom ! =null) removeNode(dom);
}
Copy the code

1.3.3 removeNode

DOM removeChildren is called to complete the node node deletion.

export function removeNode(node) {
  let parentNode = node.parentNode;
  if (parentNode) parentNode.removeChild(node);
}
Copy the code

1.4 getDomSibling

The getDomSibling function is used to get the sibling node whose index is index and the adjacent node of the current node.

  • When childIndex is equal to NULL, obtain the adjacent nodes of the current node.
  • When childIndex exists, the node where children[I]._dom exists is searched from the index, and the _dom of the node is returned.
  • In the previous step, if _dom is still not found, the query can only continue from the parent node of the current node to find the associated node.
export function getDomSibling(vnode, childIndex) {
  if (childIndex == null) {
   return vnode._parent
      ? getDomSibling(vnode._parent, vnode._parent._children.indexOf(vnode) + 1)
      : null;
  }
  let sibling;
  for (; childIndex < vnode._children.length; childIndex++) {
    sibling = vnode._children[childIndex];
    if(sibling ! =null&& sibling._dom ! =null) {
      returnsibling._dom; }}return typeof vnode.type == 'function' ? getDomSibling(vnode) : null;
}
Copy the code

2. Overall DIFF process

The DIff process of Preact adopts the method of deep search, starting from the follower node, recursively traversing the children node/component, and completing dom generation and transformation on the leaf node, thus completing the diff process. We first look at the functions of the diffElementNodes/diffChildren, in subsequent analyses preact diff of the whole process.

2.1 diffElementNodes

From the function name, we know that diffElementNodes is used to compare element nodes and generate DOM nodes. The incoming parameters of diffElementNodes are as follows:

The process of diffElementNodes functions is as follows:

2.1.1 initialization

Parameter initialization. Initialize the oldProps, newProps, and isSvg properties.

let i;
let oldProps = oldVNode.props;
let newProps = newVNode.props;
isSvg = newVNode.type === 'svg' || isSvg;
Copy the code

2.1.2 excessDomChildren

The excessDomChildren attribute is used for the reuse of existing DOM nodes in the hydration mode, and its basic functions and functions are reviewed from an overall perspective. Here is a simple Demo to show the overall function of excessDomChildren.

When excessDomChildren exist, look for the value of the current Nodes, compare with the DOM, and reuse the existing DOM nodes (diffElementNodes). According to the node type, it can be divided into three types:

  • When the nodeType is text section (nodeType === 3), the existing text node is reused.
  • Child. localName === newvNode. type when the node type is non-text, that is, when the tag name is the same, such as div and span elements.
  • When the DOM node is the same as the Child, the existing node is reused.
  if (excessDomChildren != null) {
    for (i = 0; i < excessDomChildren.length; i++) {
      const child = excessDomChildren[i];
      if (
        child != null &&
        ((newVNode.type === null
          ? child.nodeType === 3
          : child.localName === newVNode.type) ||
          dom == child)
      ) {
        dom = child;
        excessDomChildren[i] = null;
        break;
      }
    }
  }
Copy the code

When excessDomChildren exists and the children diff of the current node exists, excessDomChildren is set to childNodes of the current node.

if (excessDomChildren ! = null) { excessDomChildren = EMPTY_ARR.slice.call(dom.childNodes); }Copy the code

Reset excessDomChildren to clear excessDomChildren[I] from the DiffChildren function.

// Remove children that are not part of any vnode. if (excessDomChildren ! = null && typeof newParentVNode.type ! = 'function') { for (i = excessDomChildren.length; i--; ) { if (excessDomChildren[i] ! = null) removeNode(excessDomChildren[i]); }}Copy the code

Now, with a demo, let’s see how excessDomChildren reuses the existing DOM to complete node update and replacement. [text, div.hello-world, script, text] [text, div.hello-world, script, text]The processing and comparison process of demo excessDomChildren node is as follows:

  • When comparing div.hello-world, excessDomChildren is the initialized document.body. ChildNodes node.
    • ExcessDomChildren is not null, matching of the child. LocalName (div) = = = newVNode. Type div match, dom = document. The body. The.childnodes. Div node, Set excessDomChildren[I] to NULL.
    • If excessDomChildren is not null, the current child node excessDomChildren is set to [text, div, text(‘world’)], and the diffChildren is invoked to complete the childredn node match.
  • Compare div nodes and match div elements.
    • At this time, excessDomChildrenan matches div node according to the matching rules, dom = ExcessDomChildren.div.
    • If excessDomChildren is not null, the current child node excessDomChildren is set to [text(‘world’)], and the diffChildren is invoked to match the childredn node.
  • Compare world text nodes and match text nodes.
    • At this time, excessDomChildrenan ADAPTS to the text node according to the matching rules, dom = excessDomChildren[0], and completes the adaptation of the text node.
    • The world text node has no child nodes and returns to the node diff of the last round.
  • Div sibling node World, text node for excessDomChildrenan.
    • According to adaptation rules, the text node ADAPTS (text(“)) and does not adapt to the last node.

The main purpose of excessDomChildren is to better reuse existing DOM elements, increase the loss of node creation, and render the page faster.

2.1.3 Creating a Node

When dom is null, it indicates that the current node isA newly added node, such as isA & . In this case, three types of nodes are processed according to the node type:

  if (dom == null) {
    if (newVNode.type === null) {
      return document.createTextNode(newProps);
    }
    dom = isSvg
      ? document.createElementNS('http://www.w3.org/2000/svg', newVNode.type)
      : document.createElement(
          newVNode.type,
          newProps.is && { is: newProps.is }
        );
    excessDomChildren = null;
    isHydrating = false;
  }
Copy the code
  • Text node. If type is null, the text node is identified as the text node and you can use document.createTextNode(newProps) to create the text node.
  • SVG. Call document.createElementns (‘www.w3.org/2000/svg’, newVNode.type) to create the SVG element.
  • Element, such as div/span/input, etc. Call document.createElement to complete the creation of the node.
  • Example Reset excessDomChildren and isHydrating. Set excessDomChildren to NULL because dom is a new element and there is no need to reuse existing nodes. To create a new node, set the isHydrating to false.

2.1.4 Handling props and children

The handling of diffElementNodes in props/children can be divided into the following two aspects:

  • Text node, props.
if (newVNode.type === null) { if (oldProps ! == newProps && dom.data ! = newProps) { dom.data = newProps; }}Copy the code
  • Non-text node, according to dangerouslySetInnerHTML divided into two modes, handling props and children.
    • If using dangerouslySetInnerHTML, calculate to generate new HTML.
let oldHtml = oldProps.dangerouslySetInnerHTML; let newHtml = newProps.dangerouslySetInnerHTML; // In non-ishydrating mode, retrieve the old props. In isHydrating mode, do not handle the old props. if (! IsHydrating) {// Get the DOM attributes and copy them to oldProps. if (excessDomChildren ! = null) { oldProps = {}; for (let i = 0; i < dom.attributes.length; i++) { oldProps[dom.attributes[i].name] = dom.attributes[i].value; }} / / newHtml newHtml exists and don't want to, etc., set up the dom. The innerHTML if (newHtml | | oldHtml) {if (! newHtml || ! oldHtml || newHtml.__html ! = oldHtml.__html) { dom.innerHTML = (newHtml && newHtml.__html) || ''; }}}Copy the code
  • NewProps is compared with oldProps.
diffProps(dom, newProps, oldProps, isSvg, isHydrating);
Copy the code
  • The Children diff. DangerouslySetInnerHTML mode, set _chilren=[], otherwise compare the children of the current node.
if (newHtml) { newVNode._children = []; } else { i = newVNode.props.children; // Start a new round of recursive diffChildren(dom, array.isarray (I)? i : [i], newVNode, oldVNode, globalContext, newVNode.type === 'foreignObject' ? false : isSvg, excessDomChildren, commitQueue, EMPTY_OBJ, isHydrating ); }Copy the code
  • In non-ishydrating mode, value and CHECKED attributes of the attributes are compared, generally used for input/ Textarea nodes.
if(! isHydrating) {if (
    'value' innewProps && (i = newProps.value) ! = =undefined&& i ! == dom.value ) { setProperty(dom,'value', i, oldProps.value, false);
  }
  if (
    'checked' innewProps && (i = newProps.checked) ! = =undefined&& i ! == dom.checked ) { setProperty(dom,'checked', i, oldProps.checked, false); }}}Copy the code
  • Returns the generated DOM node.

2.2 diffChildren

The entries of diffChildren are similar to diFF and diffElementNodes. The specific parameters are as follows:

2.2.1 Initialization parameters

DiffChildren defines common parameters, including oldNode, childVNode, newDom, firstChildDom, refs, and so on.

let i, j, oldVNode, childVNode, newDom, firstChildDom, refs; // Get the _children of the old node and set it to an empty array if it does not exist. let oldChildren = (oldParentVNode && oldParentVNode._children) || EMPTY_ARR; Let oldChildrenLength = oldchildren.length;Copy the code

2.2.2 oldDom set

If oldDom does not exist, EMPTY_OBJ will be set only when render or diffElementNodes is called. In this case, the existing excessDomChildren and oldParentVNode need to be reused to get a new oldDom.

If (oldDom == EMPTY_OBJ) {//excessDomChildren exists, oldDom sets the first node. if (excessDomChildren ! = null) { oldDom = excessDomChildren[0]; } else if (oldChildren) {// if oldChildren exists, take the virtual node dom oldDom = getDomSibling(oldParentVNode, 0); } else {// Does not exist, set to null oldDom = null; }}Copy the code

2.2.3 Cyclic processing of children

The next step is to loop through the renderResult, adding the new child node to newparentvNode._children.

newParentVNode._children = [];
for (i = 0; i < renderResult.length; i++) {
}
Copy the code

The following describes the processing process of the children node according to the process of node creation and XX.

2.2.3.1 Creating a Node

Vnodes are created and generated based on different types of nodes.

  • ChildNode is null, typeof childVNode == ‘Boolean’. Nodes of this type, set to NULL, do not render.
  • A node whose Typeof childVNode is string/number will be created as a text node (vnode.type = null).
  • When childVNode is an array, it is created as a Fragment node (vnode.type = Fragment).
  • If childvNode._dom exists or childvNode._component is not null, recreate the childvNode.type node.
  • When childVNode is created, copy it directly to newParentvNode._children.
childVNode = renderResult[i]; if (childVNode == null || typeof childVNode == 'boolean') { childVNode = newParentVNode._children[i] = null; } else if (typeof childVNode == 'string' || typeof childVNode == 'number') { childVNode = newParentVNode._children[i] = createVNode( null, childVNode, null, null, childVNode ); } else if (Array.isArray(childVNode)) { childVNode = newParentVNode._children[i] = createVNode( Fragment, { children: childVNode }, null, null, null ); } else if (childVNode._dom ! = null || childVNode._component ! = null) { childVNode = newParentVNode._children[i] = createVNode( childVNode.type, childVNode.props, childVNode.key, null, childVNode._original ); } else { childVNode = newParentVNode._children[i] = childVNode; }Copy the code

After the node is created, if the value is NULL, skip this operation. If the value is not null, parentNode and depth are bound.

If (childVNode == null) {continue; } // Bind the parent node and set _depth. childVNode._parent = newParentVNode; childVNode._depth = newParentVNode._depth + 1;Copy the code

2.2.3.2 Key node processing

Preact performs specialized processing logic for nodes with a key value set.

  • Set oldChildren[I] = undefined if oldNode exists and the key/type of the new node is the same as that of the oldNode.
  • In other scenarios, oldChildren is recursively processed, looking for a node that matches key/value, and setting it to undefined if found.
oldVNode = oldChildren[i];
if (
  oldVNode === null ||
  (oldVNode &&
    childVNode.key == oldVNode.key &&
    childVNode.type === oldVNode.type)
) {
  oldChildren[i] = undefined;
} else {
  for (j = 0; j < oldChildrenLength; j++) {
    oldVNode = oldChildren[j];
    if (
      oldVNode &&
      childVNode.key == oldVNode.key &&
      childVNode.type === oldVNode.type
    ) {
      oldChildren[j] = undefined;
      break;
    }
    oldVNode = null; }}Copy the code

2.2.3.3 Diff invocation

When oldNode is null/undefined, reset to EMPTY_OBJ and call diff to generate newDom. oldVNode = oldVNode || EMPTY_OBJ;

newDom = diff(
  parentDom,
  childVNode,
  oldVNode,
  globalContext,
  isSvg,
  excessDomChildren,
  commitQueue,
  oldDom,
  isHydrating
);
Copy the code

2.2.3.4 RefS processing

ChildVNode’s ref is added to the refs, oldVNode’s REF is set to null.

if ((j = childVNode.ref) && oldVNode.ref ! = j) { if (! refs) refs = []; if (oldVNode.ref) refs.push(oldVNode.ref, null, childVNode); refs.push(j, childVNode._component || newDom, childVNode); }Copy the code

2.2.3.5 Insertion of newDom

The new DOM nodes returned by diff fall into two categories, depending on whether they are null or not:

  • If newDom is not null, perform the following steps:
    • Set firstChildDom to the first DOM node that is not null.
    • PlaceChild is called to insert the DOM node into parentDom.
    • For select.options special handling, set its value to “”.
    • If newparentvNode. type is a function, set newparentvNode. _nextDom.
  • When newDom is null, oldDom obtains the DOM of the next node.
if (newDom ! = null) { if (firstChildDom == null) { firstChildDom = newDom; } oldDom = placeChild( parentDom, childVNode, oldVNode, oldChildren, excessDomChildren, newDom, oldDom ); if (newParentVNode.type == 'option') { parentDom.value = ''; } else if (typeof newParentVNode.type == 'function') { newParentVNode._nextDom = oldDom; } } else if ( oldDom && oldVNode._dom == oldDom && oldDom.parentNode ! = parentDom ) { oldDom = getDomSibling(oldVNode); } placeChild is mainly responsible for inserting and moving the newly generated node and completing the generation of parentDom. function placeChild( parentDom, childVNode, oldVNode, oldChildren, excessDomChildren, newDom, oldDom ) { let nextDom; // In Fragment, _nextDom is not null. if (childVNode._nextDom ! == undefined) { nextDom = childVNode._nextDom; childVNode._nextDom = undefined; // Currently, only when excessDomChildren and oldNode are null, Both equal to / / dom is not equal to the old and new, newParent parentNode null} else if (excessDomChildren = = oldVNode | | newDom! . = oldDom | | newDom parentNode = = null) {/ / oldDom is null, the oldDomParentNode! = parentDom, insert the newDom to newDom outer: if (oldDom = = null | | oldDom. ParentNode! == parentDom) { parentDom.appendChild(newDom); nextDom = null; } for (let sibDom = oldDom, j = 0; let sibDom = oldDom, j = 0; (sibDom = sibDom.nextSibling) && j < oldChildren.length; j += 2 ) { if (sibDom == newDom) { break outer; }} parentdom.insertBefore (newDom, oldDom); nextDom = oldDom; } } if (nextDom ! == undefined) { oldDom = nextDom; } else { oldDom = newDom.nextSibling; } return oldDom; }Copy the code

2.2.4 Uninstallation Process

After the diff of children is completed, excessDomChildren, oldChildren, and REF are processed, existing nodes are unloaded, and newly added nodes are updated.

// Update newparentvNode. _dom to the updated dom newparentvNode. _dom = firstChildDom; // Update newparentvNode. _dom to the updated dom newparentvNode. _dom = firstChildDom; If (excessDomChildren! = excessDomChildren! = excessDomChildren! = null && typeof newParentVNode.type ! = 'function') { for (i = excessDomChildren.length; i--; ) { if (excessDomChildren[i] ! = null) removeNode(excessDomChildren[i]); }} for (I = oldChildren; i--; ) { if (oldChildren[i] ! = null) unmount(oldChildren[i], oldChildren[i]); } // If (refs) {for (I = 0; i < refs.length; i++) { applyRef(refs[i], refs[++i], refs[++i]); }}Copy the code

2.3 Overall process of DIFF

The DIff process of Preact takes the form of deep search and comparison. From top to bottom, diff, diffChildren and diffElementNode are called in a loop to complete the comparison and processing of the entire Vnode DOM tree. As an example, let’s look at the basic DIFF process during initialization.As shown in the figure below, the basic call flow is as follows: solid line represents function call, dotted line represents return and DOM processing. As we can see, Preact starts from App node, uses deep search, recursively calls diffChildren, Diff, and diffElementNodes, completes diff of node, creation of virtual node, generation of DOM, and finally inserts content into the page.

3, summarize

This section mainly introduces the DIFF process of PreACT 10.4.6, and demonstrates the creation, comparison, Dom generation and creation process of render phase nodes with a simple demo. Preact takes the form of deep search to complete the comparison of Vnode. On this basis, various methods such as clipping (key) and DOM node reuse are used to optimize the existing DIFF method and improve the efficiency of DOM diff.

4. Reference documents

  • Github.com/jamiebuilds…
  • Babeljs. IO/docs/en/bab…
  • github.com/babel/babel