Diff algorithm

Disclaimer: If you have any objection to this article, then please write your comments in the article comments at. If you find this article interesting, please share and forward, or you can look at, you acknowledge and encouragement of our articles. Wish everyone in the programming this road, farther and farther. https://blog.csdn.net/weixin_44369568/article/details/91488905

Diff algorithm

What is the Diff algorithm?

diff algorithm as a Virtual DOM accelerator that improves the optimization algorithm is the basis React and performance of the entire interface rendering of security, but also the source React most mysterious, the most incredible part

Traditional Diff:

Computing a tree structure to another tree-like structure is converted to require a minimum of steps, if conventional algorithms by comparing diff recursive traversal cycle nodes, to achieve the degree of complexity O (n ^ 3), where n is the total number of nodes, efficiency is very low, suppose we want to show 1000 nodes, then we will compare billions of times in sequence.

The following simple conventional attachment Shang Yize diff algorithm:

let result = [];
// 比较叶子节点
const diffLeafs = function (beforeLeaf, afterLeaf) {
    // 获取较大节点树的长度
    let count = Math.max(beforeLeaf.children.length, afterLeaf.children.length);
    // 循环遍历
    for (let i = 0; i < count; i++) {
        const beforeTag = beforeLeaf.children[i];
        const afterTag = afterLeaf.children[i];
        // 添加 afterTag 节点
        if (beforeTag === undefined) {
            result.push({ type: "add", element: afterTag });
            // 删除 beforeTag 节点
        } else if (afterTag === undefined) {
            result.push({ type: "remove", element: beforeTag });
            // 节点名改变时,删除 beforeTag 节点,添加 afterTag 节点
        } else if (beforeTag.tagName !== afterTag.tagName) {
            result.push({ type: "remove", element: beforeTag });
            result.push({ type: "add", element: afterTag });
            // 节点不变而内容改变时,改变节点
        } else if (beforeTag.innerHTML !== afterTag.innerHTML) {
            if (beforeTag.children.length === 0) {
                result.push({
                    type: "changed",
                    beforeElement: beforeTag,
                    afterElement: afterTag,
                    html: afterTag.innerHTML
                });
            } else {
                // 递归比较
                diffLeafs(beforeTag, afterTag);
            }
        }
    }
    return result;
}

React Diff optimization strategy map

image

  • React update phase will ReactElement type judgment different operations; ReactElement i.e. comprises three types: text, Dom, assembly;
  • Each type of treatment element update:
    • Since the update custom elements, mainly update node render out, washing his hands of the treasurer to the corresponding component render a node to manage updates.
    • Update text node is simple, direct copy update.
    • Update the basic elements of the browser, is divided into two:
      • Update properties, before and after comparison of different attributes, partial update. And handle special properties, such as binding event.
      • Updating child nodes, child nodes update the main target is to find the differences, find the difference when the object will use the above shouldUpdateReactComponent to determine if it is possible to directly update will recursively update call child nodes, such differences will recursive search object. Before deleting the object of not directly update or add new objects. After the operation the object based on the difference dom element (position change, delete, add, etc.)

React to achieve the Diff algorithm

React Diff:

Said before, React virtual mapping technology DOM DOM true, that is the difference React Diff algorithm to find the essence of the difference of two JavaScript objects to find;

Based on three strategies:

1.Web UI中DOM节点跨层级的移动操作特别少,可以忽略不计。(tree diff)
2.拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结。(component diff)
3.对于同一层级的一组子节点,它们可以通过唯一ID进行区分。(element diff)
For more than three tactics, react respectively tree diff, component diff, element diff optimization algorithm.
1.tree diff

Based Strategy One, WebUI in the DOM nodes and inter-level mobile operating less negligible, React perform hierarchical control of Virtual DOM tree, only to compare the same level of DOM node, all child nodes under the same parent element, when the discovery node does not exist, it will delete all child nodes of this node, does not compare. So only need to traverse a DOM tree, you can complete the entire tree of the comparison. Complexity becomes O (n);

Question: When we cross the DOM node-level operations, diff will be how kind of performance?

As shown below, A entire node and its children are moved to go below node D, 由于React只会简单的考虑同级节点的位置变换,而对于不同层级的节点,只有创建和删除操作,so when the root node A was found disappeared, node A will delete its child nodes, when D found in a child node of A, It creates a new a as its child nodes.
In this case, the implementation of diff are:

createA-->createB-->createC-->deleteA

image

Tree DIFF is to traverse each layer of the tree, if a component does not exist, it will be directly destroyed. As shown in FIG old genus left, the right is a new genus, the first layer is a R component, exactly the same, do not change; the second layer into the Component DIFF, the same type of comparison continues down assembly, the component A is not found, so the direct Deletion A, B, C component; continue third layer recreate A, B, C components.

It can be found that when the level of mobile nodes appear across mobile operating imagined does not appear, but will be deleted, re-create the action, which is a very React affect operating performance. So do not recommend cross-official-level operating DOM node.

2.componnet diff

React is based on the components to build applications for the strategy adopted by the comparison between the components is very simple and efficient.

  • If the same types of components, proceed according to the original Virtual DOM compare strategies.

  • If not the same type of component, it is determined that the dirty component, thereby replacing all child nodes of the entire monovalent group.

  • If the same types of components, it is possible to go through a Virtual DOM more down, and has not changed. If we can know in advance exactly that, then you can save a lot of time diff operation. Thus, React allows the user shouldComponentUpdate () to determine whether the component needs to be diff Algorithm.

As shown below, when the assembly unit D becomes G, even if the structure is similar to the two components, 一旦React判断D和G是不用类型的组件,就不会比较两者的结构,而是直接删除组件D,重新创建组件G及其子节点。although when the two components are structurally similar but different types, for analysis will affect the performance of the algorithm diff, but after all the different types of components present DOM tree of similar cases rarely occur in the actual development process, so this factor extremely difficult to have a significant impact on the actual development process.

image

3.element diff

When a node belonging to the same hierarchy, diff operation provides three nodes, respectively INSERT_MARKUP (insertion), MOVE_EXISTING (movement), REMOVE_NODE (deleted).

  • INSERT_MARKUP: New Old not set component type, i.e. the new node is required to insert a new node.
  • MOVE_EXISTING: Old collection of new types of components, and the element type is updated, this time you need to do to move operation, you can reuse the previous DOM node.
  • REMOVE_NODE: old component types in the new collection also has, but it is different from the corresponding element can not be directly reuse and update, delete operation needs to be performed, or is not new collection of old components inside, also need to perform the removal.

image

Element DIFF followed more unified type assembly continues to compare it, is a common type of list. With a list of three old becomes new behavior, insert, move and delete its comparative strategy is to specify a list for each key, 先将所有列表遍历一遍,确定要新增和删除的,再确定需要移动的。as shown in Figure D will delete the first step, the second step to increase E, performed again when A and B only need to move position.

React in Diff algorithm code

_updateChildren: function(nextNestedChildrenElements, transaction, context) {
    var prevChildren = this._renderedChildren;
    var removedNodes = {};
    var mountImages = [];
    // 获取新的子元素数组
    var nextChildren = this._reconcilerUpdateChildren(
      prevChildren,
      nextNestedChildrenElements,
      mountImages,
      removedNodes,
      transaction,
      context
    );
    if (!nextChildren && !prevChildren) {
      return;
    }
    var updates = null;
    var name;
    var nextIndex = 0;
    var lastIndex = 0;
    var nextMountIndex = 0;
    var lastPlacedNode = null;
    for (name in nextChildren) {
      if (!nextChildren.hasOwnProperty(name)) {
        continue;
      }
      var prevChild = prevChildren && prevChildren[name];
      var nextChild = nextChildren[name];
      if (prevChild === nextChild) {
        // 同一个引用,说明是使用的同一个component,所以我们需要做移动的操作
        // 移动已有的子节点
        // NOTICE:这里根据nextIndex, lastIndex决定是否移动
        updates = enqueue(
          updates,
          this.moveChild(prevChild, lastPlacedNode, nextIndex, lastIndex)
        );
        // 更新lastIndex
        lastIndex = Math.max(prevChild._mountIndex, lastIndex);
        // 更新component的.mountIndex属性
        prevChild._mountIndex = nextIndex;
      } else {
        if (prevChild) {
          // 更新lastIndex
          lastIndex = Math.max(prevChild._mountIndex, lastIndex);
        }

        // 添加新的子节点在指定的位置上
        updates = enqueue(
          updates,
          this._mountChildAtIndex(
            nextChild,
            mountImages[nextMountIndex],
            lastPlacedNode,
            nextIndex,
            transaction,
            context
          )
        );
        nextMountIndex++;
      }
      // 更新nextIndex
      nextIndex++;
      lastPlacedNode = ReactReconciler.getHostNode(nextChild);
    }
    // 移除掉不存在的旧子节点,和旧子节点和新子节点不同的旧子节点
    for (name in removedNodes) {
      if (removedNodes.hasOwnProperty(name)) {
        updates = enqueue(
          updates,
          this._unmountChild(prevChildren[name], removedNodes[name])
        );
      }
    }
  }

Based on the proposed development Diff

Based on tree diff:

  • When developing assembly, to keep the stable DOM structure; i.e., DOM dynamic operation as little as possible structure, in particular a mobile operation.
  • When the number of nodes is too large or too many times to update the page, the page Caton phenomenon will be more obvious.
  • Then you can hide or show nodes by CSS, not really DOM nodes are added or removed.

Based on component diff:

  • Note the use of shouldComponentUpdate () components to reduce unnecessary updates.
  • For the structure should be as similar to the package assembly, both to reduce the amount of code, but also reduce the performance overhead component diff.

Based element diff:

  • For the list structure, minimize similar to the last node moves to the head of the list of actions that, when the number of nodes is too large or too frequent update operations, to a certain extent influence the rendering performance of React.

Guess you like

Origin blog.csdn.net/weixin_44369568/article/details/91488905