🗂

React フラグメントを使うのは避けよう

2021/05/28に公開
6

次の記事を読んで気になったことを検証した結果、思い込みが明らかになったので書いておきます。

https://zenn.dev/uhyo/articles/usememo-time-cost

Web のハナシであり、 React Native での検証などはやっていません。

TL;DR

  • スタイルを当てないからといって React フラグメントを使うとけっこう遅くなる
    • 元記事の通り div を削ろうとして React フラグメントを使うのは逆効果なのでやめよう
  • ベンチマークは平均ではなく、中央値やパーセンタイルで比較しよう
  • ベンチマークは production ビルドで比較しよう

React フラグメントとは何か?

https://ja.reactjs.org/docs/fragments.html

こういうやつです。

function Fragment() {
  return (
    <>
      <p>ひとつめ</p>
      <p>ふたつめ</p>
    </>
  );
}

React コンポーネントはルートの要素がひとつでないといけないという制限があるため、 React フラグメントを用いてまとめることが可能です。

代替手段というか、自然と手が動く書き方は div を使うものです。

function Div() {
  return (
    <div>
      <p>ひとつめ</p>
      <p>ふたつめ</p>
    </div>
  );
}

この場合、てっきり div のほうが重い処理だと思いこんでいたことが判明しました。

ベンチマーク結果

https://github.com/januswel/react-benchmark

次は prodution ビルドして検証したものです。 DivFragment の項目を見比べてみてください。

median が中央値、 p70 が 70 パーセンタイルです。

中央値は同じ値ですが、 70 パーセンタイルについては React フラグメントを使うコンポーネントが大幅に大きいですね。これはより不安定ということです。ちょっぱやで終わることもあるけど、大抵の場合 div より時間がかかるという結果です。

中央値と 70 パーセンタイルで比較しよう

さて、なぜ中央値と 70 パーセンタイルで比較しているのかについてです。

ブラウザーだけでなく様々なプロセスや I/O が稼働しているコンピューター上でこういった細かいベンチマークを取る場合、些細なことで数値が変動します。平均値はこういった数値変動の影響を受けやすいため、単純比較するのは避けましょう。

中央値ははずれ値の影響が少ない、つまりこういった変動がある環境下での計測による性能比較に向いています。

また、どの程度安定して処理できるのかという点については、平均値付近の値の散らばりを表す分散が使えるのですが、これも平均値同様、はずれ値の影響を受けやすいため、パーセンタイルを使います。

コンピューター上の計測では、割と高い頻度で計測値が変動するため 99 パーセンタイルや 95 パーセンタイルでははずれ値の影響を無視しきれないな、というのが個人的な感覚です。そのため、この記事では 70 パーセンタイルで比較しています。

production ビルドで比較しよう

実際に使われる環境に近い条件で比較したほうが嬉しいからです。

ちなみに production ビルドせずに react-scripts でホストしたときの結果は次です。

中央値でも React フラグメントのほうが大きい値となっています。

70 パーセンタイルの値はどちらの結果でも +0.05 程度です。ベースの数値が低い分、 production ビルドでこの差が大きく響く形となっています。

ベンチマークの取得方法

このへんを見てください。

https://github.com/januswel/react-benchmark/blob/main/src/BenchmarkSuite.jsx

react-component-benchmark パッケージ、便利ですが、こういった感じでユーティリティーを整備してあげたほうが普段使いしやすいですね。

おまけ

BaseLine 、 UseMemo 、 ExtraDiv は元記事と同様のものを修正して数値をとったものです。 production ビルドでは余計な div に対して神経質になる必要はなさそうです。

どこかのタイミングでベンチマークとりながらリファクタリングすればいいんじゃないでしょうか。

GitHubで編集を提案

Discussion

RisaRisa

興味深い比較ありがとうございます!
記事中で Flagment と書いていましたが実際は Fragment なので念のためお伝えします!

janusweljanuswel

ご指摘ありがとうございます
なおしました!
お恥ずかしい…

mactkgmactkg

そもそもFragmentってなんであるんだっけ? というところが気になってドキュメントを眺めていたのですが、ドキュメントには一言も「パフォーマンスのために作った」なんて書いていないのですね。
ValidなHTMLを書きやすくするための工夫だったのでした。。。(私はなぜか、綺麗なHTMLを出力するためのものだったり、パフォーマンスの観点で有利なコンポーネントを書くためと認識していました)

https://ja.reactjs.org/docs/fragments.html

janusweljanuswel

私はなぜか、綺麗なHTMLを出力するためのものだったり、パフォーマンスの観点で有利なコンポーネントを書くためと認識していました

僕もその認識でした
また、同様に React コンポーネントのレンダリング速度の計測がすっぽり抜け落ちていたので、念のためにやってみたら面白い結果が出たので書いてみました

ksk1015ksk1015

divとfragmentのパフォーマンス比較はおもしろいですが、この調査から僕が思うのは、「React フラグメントを使うのは避けよう」ではなく、この程度の差ならパフォーマンスのためと言ってfragmentが適切なところをあえてdivで囲むなんてしなくていいってことですかね。

janusweljanuswel

そこはプロジェクトの性能要件や個人の好みで判断すべきところですね
個人的には、大きめのプロダクトであれば積もり積もって無視できないパフォーマンスになるかな、と感じています