2022-11-14

【React,Gatsby,MUI】モバイル判定コードはダメ!?サイトが劇的に重くなるという話。+解決策。

以前より本HPのモバイルのスコアが異様に低かったため、調査したところモバイル判定コードがボトルネックだと判明。コードを使わずにレスポンシブ対応する。

Article Image
使用ソフトウェアバージョン備考
Gatsby.js5.0.1
React18.2.0
MUI5.10

備忘録記事

こちらは備忘録記事であり、厳密に調査した記事ではない。

したがって解釈が誤っている箇所やもっと良い解決策があるかもしれないのでご理解頂いた上で読んでもらえればと思う。

モバイルサイトが重い

本サイトのモバイルサイトのスコアはかねてから低かった。

例えばローカルの早い環境で性能評価(Lighthouse)しても

Lighthouseの画像

真っ赤である。

しかしコレに関しては、そもそもスコア自体が低めに出る(低スペでアクセスする前提のスコア)と言われており限界だと考えていた。

あれ?軽くなるかも?

そもそも以前より様々な対策をしていたが、Gatsbyが5になったタイミングで「どのコンポーネントが重いのか」を試していたところ1つ発見があった。

モバイルorデスクトップ判定で表示/非表示がスイッチするコンポーネントを削る度スコアが目に見えて上がるという点。

これはもしかして...

レスポンシヴ対応の実装が悪いのかも

という仮説に至った。

どういう実装だったのか

そもそもどういう実装だったのか。

{isMobileSize &&
<>
    <MobileAds />
</>
}

こんな風にモバイルかどうかという判定をJSで行い、それをJSXの条件にしてタグをレンダしているだけである。

たったこれだけに気づくために何日も消費したがこれがダメだった。

判定用のコード次第じゃない?

通常は判定のコードが悪いと疑うと思う。

次の3種類を別々に実装して性能を調べた。

  • MUIのuseMediaQuery()を使う
  • windowオブジェクトを使う
  • react用のレスポンシヴ対応パッケージを使う

詳しくは省略するが、そのどれもがダメだった。

なぜダメなのか

1つだけ明らかなのは、どの方法を使ってもレンダー完了後にtrue/falseが帰るという点だ。

つまり、このコードをトリガーにコンポーネントを読み込んでいると

1度レンダ完了してからモバイル/デスクトップでスイッチするコンポーネントを読みに行くということになっている(おそらく)

windowオブジェクトをwrapRootElementというかなり親側に近い場所で仕込んでも遅いということがわかった。

つまりブロックが発生していた

というわけでロードがブロックされておりサイトが遅いと評価されていたと結論付けた。

MUIのResponsive valuesで解決

結果的にMUIResponsive valuesを使うことで遅延していたと思われる箇所が殆どなくなった。

Usage - MUI System

コレは何をしているのかというと、要するにsxのスタイリングを画面サイズ別で分けるものだ。

実際にここで稼働しているものから一部取り上げると

  <Box sx={{
      display: "none",
      [theme.breakpoints.down('xl')]: {
          display: 'flex',
          height: "100px"
      },
  }}>
  </Box>

このように特定の画面サイズで表示が切り替わる使い方。

今回はこの解説ではないので詳しくは書かないが、とにかくこれでよかった。

コードが汚れるのでは?

上の例を使って表示/非表示を切り替えるには殆どの人がdisplay: "none"を使うだろう。

Reactではわざわざコレを使うとタグそのものは描画されたまま見た目だけ消えるのでよろしくないと考えていた。

だが、上のようにJSXの条件文で切ってしまうと逆にスコアが下がるということがわかった。

MUIしかだめなんですか?

あまり詳しくないので正直わからないのだがMUIのスタイリングはCSSのコードをつかいやすくしただけではないだろうか?

というわけでメディアクエリというものがCSSで使えそうだ(あくまで予想)

メディアクエリーの初心者向けガイド - ウェブ開発を学ぶ | MDN

Bootstrapのブレイクポイントなども使えるのではないだろうか。

Breakpoints (ブレイクポイント) · Bootstrap v5.0

こちらはすべて試していないので参考までに

JSXを使ったほうが良い箇所

私のサイトではGoogle AdsenseコードにはJSXの条件でレンダリングを継続している。

そもそも広告はdisplay: "none"を使わないようにとの言及がある上に、これを使ってしまうと広告コード自体がエラーを吐いて画面全体が真っ白になるからだ。

ただし、上で述べた通りJSX側でモバイル判定して挿入しているのでサイトのスコアを落とす要素になっている。

どれぐらい早くなった?

最初に上げた画像と比較すると

Lighthouseの画像

TBTは半減、LCPも1/3になるというとんでもないスコアアップだ。

結果

サーバーの性能や混雑具合でスコアはブレるので注意。

さいごに

まさかたったこれだけでスコアが改善するとは思っていなかった。

1枚目の画像ですでに解決済みではあるのだが、遅れて表示非表示される箇所が減ったためCLSも大幅に向上している。

なんかロード時もっさりしている人は是非ためしてみてほしい。


この記事のタグ


謎の技術研究部 (謎技研)