浅析React Fiber


ことばを引く
reactがみんなの視野に入る初めに、Virtual DOM(VDOM)の概念は目の前を明るくさせて、本当のDOMを操作する前に、まずVDOMの前後を通じて(通って)更新の部分を得て、更に真実なDOMを操作して、ブラウザーの何度もDOMを操作するコストを減らしました.このプロセスは、公式の名前は、 に翻訳されます.しかしreactは今日まで発展して、先端の応用の級がますます大きくなるにつれて、reconciliationはすでに日に日に疲れが現れて、React Fiberは出荷するべきです.React FiberはReactコアアルゴリズムの書き換えであり、Reactチームは2年以上かけて完成した.
動機
拍手を浴びた当時のVDOMは、なぜ今日は少し疲れ気味だったのか、仕事の原理からも語られています.reactリリース当初は、将来のUIレンダリングは非同期であることが想定されていたが、setState()の設計とreact内部の事務機構からこの点がわかる.はい、react@16以前のバージョンでは、reconciler(現時点ではstack reconcilerと呼ばれる)は、ボトムコンポーネントまたはsetState()の後のコンポーネントから、サブツリー全体を更新するために使用されている.コンポーネントツリーが大きくなければ問題ないですが、コンポーネントツリーが大きくなると、再帰的な遍歴のコストが高くなり、メインスレッドを占有し続けます.このように、メインスレッド上のレイアウト、アニメーションなどの周期的なタスクとインタラクティブ応答はすぐに処理できなくなり、トンカードの視覚効果をもたらします.
理論的には、人間の目の最大識別可能なフレーム数は30フレームを超えず、映画のフレーム数は多く24に固定されています.ブラウザの最適なフレームレートは60で、つまり16.5 msぐらいにレンダリングされます.ブラウザの正常なワークフローはこのようなものであるべきです.演算->>レンダリング->演算->レンダリング....
しかし、JSの実行時間が長すぎると、このような状態になり、FPS(毎秒表示フレーム数)が低下して視覚的なトンカードがもたらされる.
この問題をどう解決すればいいですか?これはfiber reconcilerのすることです.簡単に言えば、下記の図を見て、実行するJSを分解して、メインスレッドをブロックしないようにしてください.
仕事の原理
同期タスクを分割することはみんな理解できますが、分割する前に以下のいくつかの問題に直面しています.
  • は何を壊しますか?
  • はどうやって分解しますか?
  • 分割後の実行順序はどうですか?
  • 何を壊しますか
    # React@15
    DOM   DOM  
    -------
    Instances React     VDOM tree node
    -------
    Elements    UI      (type, props)
    
    はい、react@15で、更新は主に二つのステップに分けられます.1.diff diffの実際の作業はprevInstanceとnextInstanceの状態を比較して、違いと対応するVDOM changeを探し出します.diffは本質的にいくつかの計算(遍歴、比較)であり、分割可能である(半分は後で計算する).2.patchはdiffアルゴリズムで計算した差行列を実際のDOMノードに更新する.Reactは差を計算して一回のpatchを実行するのではなく、全ての違いを計算して差行列を入れてから、patch方法を一度に実行して本物のDOM更新を完成します.
    最後のpatch段階の更新は、一連のDOM操作であり、diff後に得られたchange listによって分割することができますが、意味が大きくなく、内部メンテナンスのDOM状態と実際の不一致を招くだけでなく、体験にも影響を与えますので、diff段階に対して行うべきです.下の図からは、ReactDOMで10000個のサブコンポーネントをレンダリングする過程です.diffの実行段階ではメインスレッドが占有されており、実行が完了するまで他のI/O操作ができないことが見られます.
    どうやって分解しますか
    これにより、React Fiberの解決策を引き出し、一つのfiber単位で分割し、fiber treeはVDOM treeから構成されており、ツリー構造は完全に一致しているが、含まれる情報が異なるだけである.fiber treeノードの一部構造は以下の通りです.
    {
        alternate: Fiber|null, //  fiber         fiber, fiber         fiber 
        nextEffect: Fiber | null, //      ,     Fiber Tree         
        pendingWorkPriority: PriorityLevel, //               
    
    	stateNode: any, //    instance      
        return: Fiber|null, //    Fiber Tree      
        child: Fiber|null, //         
        sibling: Fiber|null, //       
    }
    
    Fiberは、return、child、及びsiblingの順にReactElementを処理することにより、以前の簡単なツリー構造を、シングルチェーンテーブルによるツリー構造に変化させ、より多くのノード関係を維持した.
    実行順序
    Stockは実行時に一つのtreeを単位として処理します.ファイバーは一つのfiberの単位で実行されます.Stckは同期のみ実行できます.ファイバーは、そのファイバーに対してスケジューリング処理を行うことができる.つまり、現在Fiberのシングルチェーンテーブル(Linked List)構造がA→B→Cであると仮定して、AがBに実行されて中断されると、その後B→Cを再度実行することができ、Stckの同期処理構造にとっては難しい.
    React Fiberが実行する過程で、主に二つの段階に分けられます.
  • render/reconciliation
  • comit(not interruptible)
  • 最初の段階では、トップから下に向かって完全なFiber Treeを構築し、レレンダーの過程で、以前に生成されたツリーに基づいて、work InProgressというFiber Treeを構築して、更新操作に用います.
    上の図のDOM構造にレンダリングが必要であると仮定して、初めてレンダーをした時に下図のようなFiber Treeを生成します.
    Itemの中の数値を平方演算する必要があるので、Buttonをクリックして、reactは前に作成したFiber Treeからwork InProgress Treeを構築します.構築の過程で、1つのfiberノードの単位でトップダウンして比較すると、ルートノードが変化していないことが発見された場合、そのchildポインタに従って、Listノードをworkinprogress Treeにコピーする.fiberノードを処理するごとに、reactは現在の時間スライスが十分かどうかをチェックします.現在の時間スライスが足りないと発見されたら、次の処理すべきタスクの優先度をマークし、優先度によって次の時間スライスがどのようなタスクを処理するかを決定します.
    requestIdleCallbackは、優先度の低いジョブをアイドル期間に呼び出し、request Animation Frameは、優先度の高いタスクを次のスタックフレームに呼び出させ、主スレッドが優先度でfiberユニットを実行することを保証する.優先順位は、テキストボックス入力>今回のスケジュールの終了に必要なタスク>アニメーション遷移>インタラクティブフィードバック>データ更新>が表示されませんが、将来表示されるジョブを防ぐためです.
    module.exports = {  
      // heigh level
      NoWork: 0, // No work is pending.
      SynchronousPriority: 1, // For controlled text inputs. 
      TaskPriority: 2, // Completes at the end of the current tick.
      AnimationPriority: 3, // Needs to complete before the next frame.
      
      // low level
      HighPriority: 4, // Interaction that needs to complete pretty soon to feel responsive.
      LowPriority: 5, // Data fetching, or result from updating stores.
      OffscreenPriority: 6, // Won't be visible but do the work in case it becomes visible.
    };
    
    平方演算において、reactは、順番にfiberノードを比較してList、Item 2を発見し、Item 3に変化が生じたら、対応する生成されたwork InProgress TreeにTagを打ち、effect listに送る.
    reconciliationが終了すると、ルートノードのeffect listには、DOM changeを含むすべてのside effectが記録され、第二段階で更新動作が実行され、このような流れが終了します.
    この例では、詳細なマッチングの流れは詳しく説明されていません.Lin Clarkの昨年のreact confでの講演を見ることをおすすめします.
    未来を想う
  • は今年アイスランドで開催されたJS Confを非同期的にレンダリングし、Danは非同期レンダリングの概念を言及しています.非同期レンダリングはコンポーネントを一つ一つロードするというのではなく、非同期的にロードしながら、プロセスを同期させる体験を与え、古い設備では、いくつかの加載時間を犠牲にして流暢な体験を得るということです.実はReact@16バージョンでは、非同期レンダリングはデフォルトではクローズされています.hack方式で非同期を実現できますが、テストを書いていないので、バグがあります.
  • ライフサイクルの大転換があります.react@16バージョンでは、まだ前のライフサイクル関数をサポートしていますが、公式では次のバージョンでその一部が破棄されると説明しています.render/reconciliationのプロセスでは、優先度と時間スライスの概念があるので、一つのタスクは半分以上実行される可能性が高く、他の優先度の高いタスクに置き換えられたり、時間的な理由で終了されたりする.このタスクを再実行すると、最初から実行すると、コンポーネントのいくつかのwillライフサイクルが、複数回起動されて、性能に影響を与える可能性がある.この問題を解決するために長い時間をくれました.公式は多くの参考事例を提供しています.次のバージョンにスムーズに移行できます.
  • react@16分水嶺というよりは、移行というか、多くの仕事はユーザーに予防注射を打っています.これからどうすればいいかを教えてくれます.react@17波風が立っているのです.reconciliationの書き換えはreactの未来に多くの可能性をもたらし、最近のコミュニティ討論の盛んなHooksを含めて、実はFiberが持ってくる可能性もあります.後のバージョンでは、個人的には、書き方に大きな変化があると思います.主により優れた性能サービスのためです.また、いくつかのコミュニティで発生した方案を最適化し、書き方をより人間的にする(HOCのrefs及びcontext伝達)と、よくある問題に対して公式の解決案(非同期データ処理)を与えるなどです.長所以外に、もちろん問題もあります.バージョンの反復に伴い、reactの概念はますます多くなり、初心者学習の曲線はますます険しくなることを恐れている.
    締め括りをつける
    大きなアプリケーションを扱う時は、reactの表現があまりよくないです.主な原因は計算に時間がかかりすぎて、メインスレッドがずっと占有されていて、他のタスクを処理できないからです.reactチームはこの問題を解決するために、前のStock reconciliationに代わるFiber reconciationの提案をした.FiberはStckに比べて非同期的に実行された計算プロセスを分割して、メインスレッドが占有されている状態にならないようにして、I/O操作、インタラクティブフィードバックなどの他のタスクを処理する時間があります.
    参考文献
  • は、React Fiberアーキテクチャをどう理解するか?知っていますか?
  • React Fiber-掘削金
  • は、ブラウザの基本的な動作原理を図示している.
  • React 16 fiberソースの速覧|Zindex'sブログ
  • 翻訳React Fiber現状確認–CYB–Medium
  • React Fiber𞓜暗羽轻扬
  • を完全に理解する.
  • A Caton Intro to Fiber
  • ブログ/from-jsx-to-dom.md at master·xiey/blog·GitHub
  • Sneak Peek:Beyond React 16