首頁 >web前端 >js教程 >React中Diff演算法是什麼? Diff演算法的策略及實現

React中Diff演算法是什麼? Diff演算法的策略及實現

不言
不言轉載
2018-09-28 17:27:033873瀏覽

本篇文章帶給大家的內容是關於React中Diff演算法是什麼? Diff演算法的策略及實現,有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

1、什麼是Diff演算法

  • 傳統Diff:diff演算法即差異查找演算法;對於Html DOM結構即為tree的差異查找演算法;而對於計算兩顆樹的差異時間複雜度為O(n^3),顯然成本太高,React不可能採用這種傳統演算法;

  1. ## React Diff:

  2. 之前說過,React採用虛擬DOM技術實現對真實DOM的映射,即React Diff演算法的差異查找實質是對兩個JavaScript物件的差異查找;

基於三個策略:
  • Web UI 中DOM 節點跨層級的移動操作特別少,可以忽略不計。 (tree diff)

    擁有相同類別的兩個元件將會產生相似的樹狀結構,擁有不同類別的兩個元件將會產生不同的樹狀結(component diff )

對於同一層級的一組子節點,它們可以透過唯一id 進行區分。 (element diff)React中Diff演算法是什麼? Diff演算法的策略及實現

  • 2、React Diff演算法解讀

首先需要明確,只有在React更新階段才會有Diff演算法的運用;

React中Diff演算法是什麼? Diff演算法的策略及實現

React更新機制:
    • ##React Diff演算法最佳化策略圖:

    • React更新階段會對ReactElement類型判斷而進行不同的操作;ReactElement類型包含三種即:文字、Dom、元件;
  1. 每個類型的元素更新處理方式:

  2. 自訂元素的更新,主要是更新render出的節點,做甩手掌櫃交給render出的節點的對應component去管理更新。
    text節點的更新很簡單,直接更新文案。
  • 瀏覽器基本元素的更新,分成兩塊:

#更新屬性,對比出前後屬性的不同,局部更新。並且處理特殊屬性,例如事件綁定。

子節點的更新,子節點更新主要是找出差異對象,找差異對象的時候也會使用上面的shouldUpdateReactComponent來判斷,如果是可以直接更新的就會遞歸呼叫子節點的更新,這樣也會遞歸查找差異物件。不可直接更新的刪除之前的物件或新增新的物件。之後根據差異物件操作dom元素(位置變動,刪除,新增等)。

  • 事實上Diff演算法只被調用於React更新階段的DOM元素更新過程;

    為什麼這麼說?

  • 1、 如果為更新文字類型
,內容不同就直接更新替換,不會呼叫複雜的Diff演算法:

 ReactDOMTextComponent.prototype.receiveComponent(nextText, transaction) {
    //与之前保存的字符串比较
    if (nextText !== this._currentElement) {
      this._currentElement = nextText;
      var nextStringText = '' + nextText;
      if (nextStringText !== this._stringText) {
        this._stringText = nextStringText;
        var commentNodes = this.getHostNode();
        // 替换文本元素
        DOMChildrenOperations.replaceDelimitedText(
          commentNodes[0],
          commentNodes[1],
          nextStringText
        );
      }
    }
  }
2、對於自訂元件元素:
    class Tab extends Component {
        constructor(props) {
            super(props);
            this.state = {
                index: 1,
            }
        }
        shouldComponentUpdate() {
            ....
        }
        render() {
            return (
                <p>
                    </p><p>item1</p>
                    <p>item1</p>
                
            )
        }
        
    }
  • #需要明確的是,何為元件,可以說元件只不過是一段Html結構的包裝容器,並且具備管理這段Html結構的狀態等能力;
  • 如上述Tab元件:它的實質內容就是render函數傳回的Html結構,而我們所說的Tab類別就是這段Html結構的包裝容器(可以理解為一個包裝盒子);

    • 在React渲染機製圖中可以看到,自訂元件的最後結合React Diff優化策略一(不同類別的兩個元件具備不同的結構)

    • 3、基本元素:
    1. ReactDOMComponent.prototype.receiveComponent = function(nextElement, transaction, context) {
          var prevElement = this._currentElement;
          this._currentElement = nextElement;
          this.updateComponent(transaction, prevElement, nextElement, context);
      }
      
      ReactDOMComponent.prototype.updateComponent = function(transaction, prevElement, nextElement, context) {
          //需要单独的更新属性
          this._updateDOMProperties(lastProps, nextProps, transaction, isCustomComponentTag);
          //再更新子节点
          this._updateDOMChildren(
            lastProps,
            nextProps,
            transaction,
            context
          );
      
          // ......
      }

    2. 在this. _updateDOMChildren方法內部才呼叫了diff演算法。
    3. 3、React中Diff演算法的實作
    4. _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])
              );
            }
          }
        }

      4、基於中Diff的開發建議

  • 基於tree diff:

    #########在開發元件時,請注意保持DOM結構的穩定;即,盡可能少地動態操作DOM結構,尤其是移動操作。 ############當節點數過大或頁面更新次數過多時,頁面卡頓的現象會比較明顯。 ############這時可以透過 CSS 隱藏或顯示節點,而不是真的移除或新增 DOM 節點。 ##################基於component diff###:###
    1. 注意使用 shouldComponentUpdate() 來減少元件不必要的更新。

    2. 對於類似的結構應該盡量封裝成元件,既減少程式碼量,又能減少component diff的效能消耗。

  • 基於element diff

    1. #對於清單結構,盡量減少類似將最後一個節點移動到列表首部的操作,當節點數量過大或更新操作過於頻繁時,在一定程度上會影響React 的渲染效能。

    以上是React中Diff演算法是什麼? Diff演算法的策略及實現的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    陳述:
    本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除