Closed11

読む:【React】仮想DOM~StackとFiberを理解する~

Yug (やぐ)Yug (やぐ)

https://zenn.dev/arranzt/articles/01807d1b3d2fc1

Concurrent Features、初耳

React18からConcurrent Featuresが実装されます。以前はConcurrent Modeと呼ばれていましたが、名称が変更され、それと同時にConcurrent Modeの機能の段階的な導入が可能となります。

Fiber / reconcileの仕組みは何となくわかってるつもりだけど、あれのどの部分をConcurrent Featuresと呼ぶんだろう

このConcurrent Featuresですが、ReactのFiberと呼ばれるアルゴリズムに対して提供されるAPIであることから、Reactの最新版で新たに追加される機能を理解するためには、仮想DOMの差分検出処理のアーキテクチャについて簡単に知っておく必要があります。

Yug (やぐ)Yug (やぐ)

Fiberはv16以降の話で、15以前はStackだったのか

もともとReactの仮想DOMを解析するアーキテクチャとしてバージョン15まではStack、16以降はFiberと呼ばれるアーキテクチャが使われており、リコンサイラと呼ばれる機能がそれを実現していました。

Yug (やぐ)Yug (やぐ)

へぇ、DOMはグラフ理論ってやつがモデルなんだ

DOMとは、Document Object Modelの略で、HTMLドキュメントのツリー構造をプログラムから利用するためのAPIのことです。元々は、数学のグラフ理論を背景として構築された技術になります。

グラフ理論て何?
https://ja.wikipedia.org/wiki/グラフ理論

まじでDOMみたいなやつか。
有向グラフと無向グラフがあるのね。そしてグラフは隣接行列で表現することもできる

グラフを表現するのに、図ではなく、隣接行列を用いることがある。無向グラフの隣接行列は、対称行列になる。

隣接行列はこれ

有限グラフを表わすために使われる正方行列

https://ja.wikipedia.org/wiki/隣接行列

要はノード同士の繋がりを0/1もしくは重みで表現した正方行列のことか。

対称行列っていうのは、左上から右下に対角線を引いた時に、分かれた2つが左右(?)対称になってる隣接行列のこと。無向グラフなら必ず対称行列になる。

もしグラフが無向ならば、隣接行列は対称である。

Yug (やぐ)Yug (やぐ)

アトミックデザインってコンポーネント分割的な話なのか
原子(atomic)の集まりとして捉えよう的な?ならアトミック=コンポーネントと捉えて良さそう

しかしながら、2012年頃から、webページをパーツの集まりとして考えるアトミックデザイン的な発想が登場してきます。

Yug (やぐ)Yug (やぐ)

ちゃんと分割したコンポーネント単位ですら計算量多いので、更に、コンポーネント内の更新部位のみ見るようにした、ということかな?

このような研究の結果、そのまま実装した場合、計算量が爆発的に多くなるということがわかったため、コンポーネント単位でのwebページ構築はそのままでは具合が悪い、という話になりました。
こういった文脈の中で仮想DOMの技術が登場してきます。

Yug (やぐ)Yug (やぐ)

差分を更新する訳ではない気が...

HTMLのツリー構造をDOMの形で変更前と変更後の形を保持しておいて、差分だけ更新してあげるという処理を差分検出処理(リコンシリエーション)と呼び、

差分を記録しておくだけでは?
何を消すべきかという情報をdeletion配列に記録するとか。

Yug (やぐ)Yug (やぐ)

ほーそんな感じだっけ(まぁこれはfiberではなくstackの話ではあるけど)

実際にはそれぞれの要素の型を比較し同じであれば属性を比較する、といったように再帰的に処理が進むのですが、イメージとしてはまずHtmlタグで変更がないかを見て、変更がないならその下のHeadタグに変更がないかを見て…というように順番に変更箇所を探っていきます。

たしかに再帰的に見るみたいなのはあったな
https://zenn.dev/yg_kita/scraps/3347485d5dd931

Yug (やぐ)Yug (やぐ)

ふむふむ、それがStackリコンサイラの問題点か

先程のイメージ図のように変更箇所を処理していく事は、ある意味で同期的な処理と同義であり、それゆえDOMの更新処理を途中で停止したり、更新処理の間アプリが動かなくなる、といった問題が発生していました。

へぇ

引っかかりが発生する理由はシンプルで、レンダリングにStackリコンサイラを用いているので、レンダーが一度始まってしまったら中断できないからです。このようなレンダリングの手法をブロッキング・レンダリング(Blocking Rendering)と呼び、レンダリングを待たせる(ブロックする)要因となる外部ファイルをレンダリングブロックリソースなどと呼んだりもします。

Yug (やぐ)Yug (やぐ)

その差分検出を非同期にやれるようになったのがFiber/Fiberリコンサイラのアーキテクチャなのか

へぇ、そんな柔軟なのか、すごい

例えばFiber1とFiber2が非同期的に処理され、もしFiber1の中で何らかの問題が発生した場合にはFiber2が先に解析され、そのあとでFiber1に戻って処理をするといったイメージとなります。

Yug (やぐ)Yug (やぐ)

へぇ、StackリコンサイラではなくFiberリコンサイラを使いたい!と明示的に指定するみたいなことをする目的でConcurrent Featuresとやらがv18からは登場した流れか

なお、バージョン16.13から実験的機能としてConcurrent Mode(並列モード)が導入されたものの、これを操作するためのAPIがこれまで用意されておらず、そのためユーザー側から特にリコンサイラとしてStackのものを使ったり、Fiberのものを使うといった指定をすることが出来ませんでした。
これを解決してくれるものが、React18から登場するConcurrent Featuresになります。

Yug (やぐ)Yug (やぐ)

新モードであるConcurrent Featuresでは、レンダリング用のアルゴリズムがFiberになっているおかげでレンダリングの中断処理が可能となっており、これにより各コンポーネントのレンダリングを適宜停止&再開しつつ適切にスケジューリングしてくれるようになります。

んーでもレンダーの中断はほぼ行われないらしいけどなぁ

実は、v18において中断されるケースは startTransition または Suspense を使用した場合だけのようです。

https://zenn.dev/ktmouk/articles/68fefedb5fcbdc#とはいえ、殆どのrenderフェーズは中断されない?

このスクラップは12日前にクローズされました