Open19

フロントエンドのパフォーマンス・ビルド関連に関するメモ

shin_k_2281shin_k_2281

はじめに

一部こちらの書籍を参考にしています🙇‍♂️ とてもよい本です!
https://www.amazon.co.jp/Webページ速度改善ガイド-使いやすさは「速さ」から始まる-WEB-PRESS-plus/dp/477419400X

以下随時追記したり、埋まっていない部分もありますがご了承ください🙏

用語編(基礎)

⭐ package

パッケージとはプログラムがたくさん入ったディレクトリ/フォルダーのようなもの2であり、NPM で公開されているほとんどのパッケージは外から使うためのライブラリである。世の中にあるパッケージを使えば自分で一からコードを書かなくとも高度な機能を実現することができる。

パッケージを利用するとなったときに、「直接パッケージをダウンロードして自分のプロジェクトに含めればよいのではないか」、さらには「Git リポジトリに含めてよいのではないか」と思うかもしれない。しかし、もしそのパッケージに新しいバージョンが出て、バグ修正や機能の追加がされたとき、自分のプロジェクト内のパッケージもアップデートしたくなるかもしれない。そうすると自分のプロジェクト内のファイルを手動で更新しなければならなくなる。シェルを使えば簡単に更新できるかもしれないが、全く同じコードが複数の場所(今の場合 NPM と自分の Git リポジトリ)で管理されるというのは無駄であり、「本当に自分のプロジェクトに含まれているパッケージは NPM 上のパッケージと内容が一致しているのか」という懸念も生じる。

よって外部のパッケージは自分のプロジェクトに含めるのではなく、「このプロジェクトは NPM のこのパッケージに依存している」、という依存情報だけを「宣言」するのがよいということに落ち着く。このような依存先のパッケージをこの界隈の言葉で dependency (depend=依存する)と呼ぶ。NPM ではパッケージは別のパッケージに依存し、そのパッケージがまた別のパッケージに依存し...と、パッケージが dependency のネットワークを成すことになる。

わかりやすかったので引用: https://qiita.com/righteous/items/e5448cb2e7e11ab7d477

⭐ module

A module is just a file. One script is one module. As simple as that.
Modules can load each other and use special directives export and import to interchange functionality, call functions of one module from another one:

モジュールはただのファイルです。1つのスクリプトは1つのモジュールです。モジュールは相互に読み込んだりexportとimportを使用して機能をやりとりしたり、あるモジュールの関数を別のモジュールから呼び出したりすることができます。

For a long time, JavaScript existed without a language-level module syntax. That wasn’t a problem, because initially scripts were small and simple, so there was no need.

長い間、JavaScriptは言語レベルのモジュール構文なしで存在していました。最初はスクリプトが小さくて単純だったので、それでも問題ではありませんでした。

→ <script>タグの記述によるjs間の参照はコード量が多くなってくるにつれて煩雑になり、改善が必要となったために最初は以下のようなmodule-bundlerソリューションが生まれた。

  • AMD
  • CommonJS(Node.jsで使われている)
  • UMD

参考: https://javascript.info/modules-intro

⭐ module bundler

module bundlerは文字通りモジュールを一まとめにするツール。
代表格であるwebpackでも使用されており、JavaScriptの依存関係を解決し、それら全てをincludeした一つのファイルにまとめることができる。
まとめることでHTTPリクエストの数を減らしたり、高度なWebアプリケーション開発に役立つ。

module bundlerの例

  • browserify(単機能でシンプル)
  • webpack(多機能)
  • rollup
  • parcel(WEBアプリケーションバンドラ?)

参考: https://js.studio-kingdom.com/webpack/guides/code_splitting

bundle

上述したようにモジュールを一まとめにする行為を指す。

周辺用語について

(タスクランナー)

2013年ごろのフロントエンドのトレンドとして、GulpやGruntなどのタスクランナーと呼ばれるものも存在していた。
フロントエンドの仕事で面倒なタスクを自動的にやってくれる便利なツールである。
npmではnpm scriptsの機構がこれをほぼ担っている?

(ビルドツール)

単純にコンパイラ&リンカを指す場合と、ビルドを構成するタスクを定義・実行するツールを指す場合がある。
タスク例:
・ソースファイルのコンパイル
・テストや静的解析

モジュールバンドラーやタスクランナーなどの機能を備えるものは大体ビルドツールの一種と考えてもよさそう。

参考: https://alliance7.blogspot.com/2019/03/blog-post.html

⭐ webpack

At its core, webpack is a static module bundler for modern JavaScript applications. When webpack processes your application, it internally builds a dependency graph from one or more entry points and then combines every module your project needs into one or more bundles, which are static assets to serve your content from.

基本的にwebpackは最新のJavaScriptアプリケーション用の静的モジュールバンドラーです。webpackがアプリケーションを処理するとき、1つ以上のエントリポイントから依存関係グラフを内部的に構築し、プロジェクトに必要なすべてのモジュールを1つ以上のバンドルに結合します。これは、コンテンツを提供するための静的アセットです。

参考: https://webpack.js.org/concepts/

(loaderとは)

webpackは通常JavaScriptしか解析できない。
loaderを使ってJavaScript以外のファイルも解析可能にしてバンドルを行う。

⭐ chunk

「chunk: 大きい塊」
大規模なアプリケーションにおいて、全てのコードを1つのファイルに含めてしまうのは得策とは言えず、 特定のコードのブロックがある階層下でしか必要とされないケースなどでそれが顕著に現れる。 webpackは必要に応じて、読み込む"chunks"(チャンク/固まり)ごとににコードを分割する機能を備えている。

Chunks come in two forms:
initial is the main chunk for the entry point. This chunk contains all the modules and their dependencies that you specify for an entry point.
non-initial is a chunk that may be lazy-loaded. It may appear when dynamic import or SplitChunksPlugin is being used.
Each chunk has a corresponding asset. The assets are the output files - the result of bundling.

チャンクには2つの形式があります。

  • initial: エントリポイントのメインチャンク。エントリポイントに指定する全てのモジュールとその依存関係が含まれる。
  • non-initial: 遅延ロードされる可能性のあるチャンク。dynamic importやSplitChunksPluginが使用されている場合に発生することがある。

参考: https://webpack.js.org/concepts/under-the-hood/#chunks

shin_k_2281shin_k_2281

最新のビルドツール

state of js 2021の結果を見ると、wepack以外にも様々なビルドツールがあがっていることがわかる。
https://2021.stateofjs.com/en-US/libraries/build-tools

vite

vueの開発者が作成したNo bundleツール。
モダンブラウザではESMをNativeサポートしているという考えから開発時は基本no bundleで外部ライブラリのみ事前コンパイルするという手法。

snowpack

viteと同じくno bundleなビルドツール。

(webpackやParcelなどの従来のJavaScriptビルドツールでは、1つのファイルを保存するたびに、アプリケーションのチャンク全体を再構築して再バンドルする必要があった。)

(viteとsnowpackの違い: https://ja.vitejs.dev/guide/comparisons.html#snowpack)

esbuild

Goで実装されたJavaScript/TypeScriptのツール。
トランスパイル/バンドル/minifyなどを担ってくれる。
公式ドキュメントによると速さはwebpackやRollupの数十倍らしい。

swc

Next.js v11にも組み込まれているJavaScript/TypeScriptのトランスパイラ。
Rustで実装されている。 swc単体にはバンドラーの機能はない。

(esbuildとswcにはloaderが用意されており、webpack環境で使用することができる。 )

shin_k_2281shin_k_2281

2013年ごろのフロントエンドのトレンドとして、GulpやGruntなどのタスクランナーと呼ばれるものも存在していた

Frontend developer loadmap 2022ではすでにTaskRunnerのところからgulpやgruntは消えている。

https://roadmap.sh/frontend

shin_k_2281shin_k_2281

用語編(応用)

⭐ package.json

パッケージを作成するには「package.json」が必要。
npm initをすると出来上がる。

└dependencies

実行に必要なパッケージの情報。
packageをpublishし、外部の人がインストールを行うと、このdependenciesにあるmoduleのみがインストールされる。

└devDependencies

開発やテストのみに必要なパッケージの情報。

└(peerDependencies)

lernaやyarn workspaceなどのmonorepo機構で用いられることがあるらしい。

⭐ node_modules

package.jsonによって依存関係の解決が行われると、そのpackageに必要なmodule群をまとめてnode_modulesの中にinstallする
bundlesizeの可視化を行うと、node_modules内のサイズがアプリケーションのサイズの大きい部分を占めていることもあり、パフォーマンス改善ではこちらで不要なモジュールを削除したりすることが重要となる。

⭐ Dynamic import

ES Modulesで登場した機能(?)
従来、モジュールのimportはトップレベルで宣言したのち静的に解決が行われる形だったが、ES Modulesのimport()関数を使うとスクリプトの評価時に遅延して読み込まれる。
これにより任意のタイミングでモジュールを読み込んで、ページの初期表示では必要なJSだけを読み込んでおきコンテンツが展開されるたびに必要なモジュールを遅延ロードすることで、ページの初期表示時の処理不可を軽減できる。
ReactではReact.lazyでimport()を使っておりRouterなどでこれを用いてパフォーマンス改善されることが多い。これをcode splitting

参考: https://qiita.com/tonkotsuboy_com/items/f672de5fdd402be6f065
https://ja.reactjs.org/docs/code-splitting.html

⭐ code splitting(コード分割)

bundleされるファイルを分けてページロードを早くするための手法。
分割されたコードはユーザのアクションに応じて非同期に読み込まれる。
上のようなDynamic importなどを用いてページを開いた時の初期ロードに必要なmoduleの量を減らしたりすること。

code splittingのデメリット

モジュールを分割することによりそれらを読み込むためのレイテンシが増える。

  • JS1ファイルのSPAでは初期表示に時間がかかるがそれ以降は高速な遷移が可能
  • コード分割したSPAでは初期表示は早いがそれ以降は分割したファイルを読み込むためのオーバーヘッドがかかる(prefetch/preloadで対応可能?)

アプリケーションの特性や仕様によって適切にチューニングしていくことが重要。

code splittingのパターン

代表的なもの
1. Page
→ ページ毎に分割してルートの変換が行われたら必要なページのコードを遅延読み込みする
2. Fold
→ ATF(後述)に対し、見えない部分はBTFと呼ばれるが、このBTF部分は初期表示には必要ないという考え。しかしやるとなると実装にやや工夫が必要そう。
3. Temporal
→モーダルやツールチップなど初期表示にはなく、ユーザのインタラクションなどトリガーとなるイベントと一緒に絡めて遅延読み込みするという手法

⭐ prefetch/preload

chunkしたモジュールをのローディングを最適化(事前に読み込んでおく)する機能。
(webpackでは、import()関数に対して関数の中でコメントをつけるだけでできるとのこと。)
そうすることによって<link rel="prefetch" href="hoge.js">のようなタグが最終的に生成される。
またrefetchはナビゲーションの前に動作するのに対して、 Prelaod はナビゲーションが行われた後に動作する。

⭐ tree shaking

ES Modulesにおいてimportされないモジュールを削除する機能。
tree-shakingに対応しているライブラリであれば自動的にimportしていないモジュールを除いてbundleが行われる。(はず)

CoreWebVitals

Googleが策定したサイトのユーザー体験を高めるための各指標。
これらの指標はSEOにも影響があり、toCサービスなどでは特に意識する必要がある。

└LCP (Largest Contentful Paint)


ページ内で最も大きなコンテンツがレンダリングされうまでの時間。

└FID (First Input Delay)


ユーザーが最初にページを操作したときから、ブラウザがその操作に応答するまでの時間。

└CLS (Cumulative Layout Shift)


「累積レイアウト変更」
意図しないレイアウトのずれがどれくらい発生したか。


参考
https://ja.reactjs.org/docs/code-splitting.html
https://qiita.com/seya/items/06b160adb7801ae9e66f
https://blog.jxck.io/entries/2016-03-04/preload.html

shin_k_2281shin_k_2281

適当なライブラリのサイズを実際に見てみる

前提知識

(参考: https://manuelro.medium.com/gzipping-vs-minification-what-the-heck-ba698fa6037c)

  • gzipとは
    圧縮アルゴリズムの一種。zipは圧縮アーカイバであるのに対しgzipは圧縮のみを行うらしい。

  • minifyとは

    コメント、空行、改行、セミコロンなどの実行時には必要ない要素を省いてサイズを小さくすること。

ライブラリなどには上記2つがどちらも施されている。

UIライブラリ系

CSS in JS系

→ linariaが圧倒的に小さそう

日付系

→date-fnsの方が大きそうだがこちらはtree-shakingが効くように見える

data fetch系

→やはり高機能(?)なapolloが一番大きい

その他

これだけ見ても結局この値に対する価値観がまだないので後続のwebpack bundle analyzerでもうちょっと調べてみる。


参考
https://bundlephobia.com/

shin_k_2281shin_k_2281

webpack bundle analyzerで適当なアプリのbundleとchunkを見てみる

CRAではこれとか
https://www.npmjs.com/package/cra-bundle-analyzer
Next.jsではこれとかを使うことになりそう
https://www.npmjs.com/package/@next/bundle-analyzer

Next.js(frourio)で作ったサンプルアプリを調べてみた

  • client
  • server

仕事のほう(CRA)のアプリケーションも調べてみたが、やはりNext.jsの方は勝手にchunkをページごととかに分けてくれてそう


参考

https://qiita.com/kurosame/items/9e7092cdf08ff2ba7500#バンドルファイル内の各パッケージがどのくらいの容量を占めているか知りたい

shin_k_2281shin_k_2281

パフォーマンス計測/改善について

合成モニタリング

計測用の環境を準備するなどして同じ条件で定期的に計測を行うモニタリング手法。
条件が固定された環境で繰り返し計測することで計測ごとの揺らぎが抑えられる。
また、計測時は具体的なレポートを取得できるため具体的な改善にも役立つ。
モバイルなどの悪条件での閲覧を想定して、低速な帯域条件を指定しておくとよい。

リアルユーザーモニタリング

合成モニタリングだけでは特定環境からの計測にすぎないため、リアルユーザーモニタリングでは実際のエンドユーザーが本当に十分な速度でwebページを閲覧できているかを確認する。
WebPagetestなどのモニタリングサービスを活用する。

パーセンタイルについて(p50とかp75みたいなやつ)

一定パーセントの観察結果がそれ未満の値になる変数の値である。
p95として示されている数値は、実際にそのメトリクス対象の95%が、その値をクリアしていることを表す。

参考: https://docs.sentry.io/product/performance/metrics/#p50-threshold

shin_k_2281shin_k_2281

RAILモデル

Googleのエヴァンジェリストによって提唱されているパフォーマンスに関する考え方を構造化するユーザー中心のパフォーマンスモデル。

  • Response (目標: 100ms)
    UI操作の応答があるまでの時間
  • Animation (目標: 10ms)
    アニメーション1frameあたりの時間
  • Idle (目標: 50ms)
    アイドル状態で行われる処理単体の時間
  • Load (目標: 1000ms)
    ページロードの時間

参考
https://web.dev/i18n/ja/rail/

shin_k_2281shin_k_2281

WEBフロントエンドを高速化のための3ポイント

ネットワーク処理

サーバーから配信されるHTML, CSS, JavaScript, 画像など各種リソースをブラウザがダウンロードする処理。ダウンロードすべきリソースの数やファイルサイズの最適化などが改善方法として挙がる。

レンダリング処理

ダウンロードしたHTMLとCSSに基づいて要素を表示する処理。
ランタイムでの画面更新に関わる各種の処理を最適化することが改善方法。

スクリプト処理

こちらもランタイムで主に問題になる、JavaScriptによる処理の遅延。
ほとんどはJavaScriptのロジックに問題があるかそれによって操作されるDOMやスタイルなどのレンダリング処理にまつわる部分の改善。

shin_k_2281shin_k_2281

ページコンテンツ表示速度に関する指標や用語について

(こちらはCore Web Vitalsに対し、単にWeb Vitalsと呼ばれたりもする)

First Paint (ページが表示され始めた時)

ナビゲーションの開始からWEBページの何らかが表示され始めたタイミングを指す。
例えば背景色がグレー表示され始めた地点など。

First Contentful Paint (コンテンツが表示され始めた時)

ナビゲーションの開始からコンテンツが表示され始めたタイミングを指す。
First Paintがとにかく何らかの要素が表示されていれば成立するのに対してFCPはSVGやCanvasを含む画像、テキストなどのコンテンツ要素の描画によって成立する。

First Meaningful Paint (ユーザに意味のある表示になった時)

FPやFCPに比べ曖昧な定義になっているが、ページがユーザーにとって意味のある表示を行なったタイミングを指す。

Time To Interactive (ユーザの操作に応答できるようになった時)

Webページの表示を終えてユーザーの入力に対してアプリケーションが応答が可能になったタイミングを指す。
SPAなどでJSの初期化、API通信に時間がかかる性質がある場合は見た目上でATFの表示が早くても実際に操作可能になるまでの時間がかかってしまうケースがある。

Time To First Byte (最初の1バイトを受信するまでの時間)

TTFBはブラウザがサーバーからのデータの最初の1バイトを受け取るまでにかかる時間のこと。
このデータを受け取るまでに時間がかかるほど、ページの表示が遅くなる。
この指標にはネットワークレイテンシも含まれ、DNSルックアップの遅延やサーバーとの地理的な距離の長さによって左右されることがある。

Speed Index (ATFの性能を表す)

ATF(Above the fold)スクロールせずに閲覧可能な画面領域を表す。
→BTF(Below the fold) はそれ以外の領域

Speed IndexはATFの表示性能を数値化したもので、ページロードの経過時間に対するコンテンツのレンダリング量をプロットし、レンダリングされなかった領域面積をスコア化する。
値が小さいほど優れている。

Total Blocking Time

reef: https://web.dev/i18n/ja/tbt/

Total Blocking Time (TBT) 指標は、長時間に渡りメイン スレッドがブロックされ、入力の応答性が妨げられることで発生する First Contentful Paint (視覚コンテンツの初期表示時間、FCP) と Time to Interactive (操作可能になるまでの時間、TTI) の間の時間の合計を測定します。

Interaction to Next Paint

https://www.suzukikenichi.com/blog/what-is-inp/
ユーザーのインタラクションに対してどれだけ早くフィードバックが現れるか

表示速度の測定

(dev toolのNetworkタブの下のところで見れる)

DOMContentLoaded

DOMツリーの構築が完了したタイミング

load

DOMツリーに起因するサブリソースの取得と評価が完了したタイミング

比較サンプル

サイト例1

サイト例2

1のサイトは2のサイトよりも、初期表示は早いが静的リソースの取得などサブリソースの処理に時間がかかっていると言える

shin_k_2281shin_k_2281

クリティカルレンダリングパス

https://qiita.com/kei4eva4/items/d310b423cf0fd73e4893#クリティカルレンダリングパス

WEBページにおいて最初のレンダリング処理が行われるまでに必要な一連の処理。
(あくまでページロード時にレンダリング処理に至るまでの時間の最適化)
この中でwebページのリクエスト、DOM構築、jsやcssのロード、DOMツリー/CSSOMツリー構築などが行われる。

サブリソースの中でCSSとJSのロード中はDOMツリーやCSSOMツリーに影響を与える可能性があるためレンダリング処理がブロックされるらしい。

shin_k_2281shin_k_2281

Chrome Developer Toolについて

chrome devtoolの機能

  • Elements
    DOMのスタイルの調査、編集

  • Console
    アプリケーション出力の表示やインタラクティブなJSの実行

  • Sources
    ロードされたCSSやJSファイルのデバッグ

  • Network
    発生したネットワーク処理の詳細表示

  • Performance
    Webページ実行時に発生するブラウザ内部処理の計測

  • Memory
    ヒープ情報の取得、スクリプト処理の詳細な計測

  • Application
    ServiceWorker, ストレージ, Cookieなどのデバッグ

  • Security
    Webページがセキュアであるかどうかの調査

  • Recorder(実験中?)
    Web上の操作をレコーディングしてその一連動作のパフォーマンス計測やPupeteer用スクリプトを吐き出せたりする
    参考: https://qiita.com/konnyaku256/items/55d8b2402bf9b16aa257

shin_k_2281shin_k_2281

Networkパネル

Timingタブ

🌟ConnectionStart

ストール / ブロッキング(Stalled)
リクエストを送信できるようになるまでの待機時間。 「キューイング(Queueing)」で示した理由で待機している場合もあります。 また、この時間にはプロキシ ネゴシエーションにかかった時間も含まれます。

DNS 参照(DNS Lookup)
DNS 参照の実行にかかった時間。 ページ上の新しいドメインはすべて、DNS 参照を行うために完全なラウンドトリップを必要とします。

初期接続 / 接続中(Initial Connection)
TCP のハンドシェイク / 再試行や SSL のネゴシエーションなど、接続の確立にかかった時間。

SSL
SSL ハンドシェイクの完了にかかった時間。

shin_k_2281shin_k_2281

Performanceパネル

開くと以下のような表示になる。

左上の丸いボタンを押したあとに、調べたい一連の行動や処理などをブラウザ上で行い、終わったらStopボタンを押す。
https://gyazo.com/ece13011ca12de0c9b89e6222280cd7d

計測が終わると以下のようなものが表示される。

計測結果のアクティビティについて
横が時間軸に沿っており、それぞれの時間に発生したイベントの情報が縦に表示されている。
マウスを上にスクロールすると時間軸を狭めることができ、情報部分もその範囲に追従して更新される。
マウスを左右に動かすと同じ範囲で時間軸をずらして調べたりもできる。
これを利用してアクティビティが活発な箇所に絞りより細かく値を見ていくなどで調査していくとよさそう。
https://gyazo.com/78941518d01ee17f6d73f9b83ce359e7

上部分の情報(FPS,CPU,NET,スクリーンショット,Heap)

1段目(ms表示と黄緑色グラフの部分)

フレームレート(FPS)を表す。
速いほど縦軸の値が大きく短期間の間に高速で画面が更新されていることを表す。
PCモニタのリフレッシュレートは一般的に60FPSであるため、この値が基本的な指標となる。

黄緑色の棒グラフが高いほどスムーズにレンダリングされている状態。
棒グラフが赤い場合はフレーム内の処理に時間がかかりすぎている状態を示す。

2段目(黄色グラフの部分)

CPUの稼働状態を表す。
グラフが高いほどCPUへの負荷が大きく、色については処理の種類を表していて
車線が引いている箇所はメインスレッド以外の処理となる。

青色 (Loading): HTTPリクエストやHTMLのパース処理
黄色 (Scripting): JavaScriptで行われた処理全般
紫色 (Rendering): スタイルの評価やレイアウト算出
緑色 (Painting): ペイント処理や画像のラスタライズ(ビットマップ化)
灰色 (Other): その他

3段目(Netと書かれている部分)

ネットワーク処理を表す。
濃い青色のものは優先度が高いリソースへのリクエスト。(HTML、CSSなど)
薄いものは優先度の低いリソースへのリクエストを示している。(画像など)

(上記の優先度は<link rel="preload> 指定、Resource hintsなどによってコントロールすることもできる。)

4段目(スクリーンショット)

各時間におけるスクリーンショットが表示される

5段目(Heap)

JavaScriptのヒープ領域のメモリ使用量を示しています。

Detailセクション

見たい範囲を絞って確認できる。
Network, Frames, Interactions, Main, Raster, GPU, ScriptStreamer thread などの情報がわかる

Network部分の横棒グラフが表しているもの

青色: HTML
紫色: CSS
緑色: 画像
黄色: JavaScript
灰色: フォント

Bottomセクション (Summaryタブ)


所要時間や全体に占める割合を表示している。
このSummaryの各イベントについては以下。

  • Loading(青) → HTTPリクエストやHTMLのパース等
  • Scripting(黄) → JavaScriptで行われた処理
  • Rendering(紫) → スタイルの評価やレイアウト算出
  • Painting(緑) → ペイント処理、ラスタライズ

Bottom-Upタブ

各アクティビティの所要時間が表示される。
ここでパフォーマンスに影響を与えている関数がわかる

Call Treeタブ

タイマーや関数の実行など、処理の起点となったアクティビティごとに分類された所要時間が表示される(?)
関数の呼び出し元から順番に処理を追うことができる。

Event Logタブ

時系列順のイベント発生ログが表示される。
コンボボックスから、表示を省略する秒数を選択できるため、重たい処理をフィルタリングするのに役立つ。

shin_k_2281shin_k_2281

show console drawer

ここを押して出てきたドロワー内の3点リーダーからさらに「Rendering」を選択すると
以下の部分にRenderingタブが現れる。

この各チェックボックスではそれぞれ、アプリケーションを実際に動かしながら様々な視点で計測が可能になる。

(例)

  • Paint Flashing
    レンダリングされた部分が緑色の枠で表示される。(React devtoolを使えるならいらなそう)
  • Layout Shift Regions
    Layout Shiftが起きた部分を青色で示してくれる。
  • Layer Borders
    GPU合成レイヤーで扱われている箇所をオレンジ色で表示してくれる。
    GPUでレンダリングすべき箇所がちゃんとそうなっているかの確認に使える。
  • Frame Rendering Stats
    FPSのリアルタイム表示が行われる。
  • Scrolling Performance Issues
    スクロールの滑らかさを阻害する要因がWebページ内に表示される。
shin_k_2281shin_k_2281

chunk分割について

CreateReactApp環境(code splittingを意識してない実装)のchunk


→分割されておらず巨大な2つのchunkがある

Next.js環境のchunk


→特に何もやってないがNext.js側で勝手にpageごと、ライブラリコード、アプリケーションコードなどで分割されてるように見える

Next.jsがコード分割をよしなにやってくれているという所以が少し理解できた。
以下で言っていることもなんとなく「そういうことか」となった。

https://qiita.com/mizchi/items/418be9abee5f785696f0

できれば webpack 推奨の 144kb 以内にしたい…が現実的に難しいので、 せめて 350kb ぐらいに抑えたい。
SPAなら (ローディングスピナーなどのアニメーションを出した上で) 1.5MB ぐらいに抑えたい。
ビルドサイズが 3MB 超えたあたりで、日本の一般的な 4G 環境では使い物にならなくなる。

shin_k_2281shin_k_2281

Web Worker

JavaScriptのメインスレッドとは別で、ワーカースレッドで処理を行うWeb WorkerというAPIがある。
ワーカースレッドからは一部、メインスレッドから実行可能な各種ブラウザAPIに対しては利用制限があるが、WebGLの座標計算、物理演算といったブラウザAPIに関与しないかつコストの高い処理はWeb Workerを利用してワーカースレッドに移譲するとよい。

window.addEventListener('message', e => {...})

window.addEventListener('click', e => {
  window.postMessage({
    command: xxxx,
    number: xxxx
  })
})

このようにmessageイベントリスナを定義し、移譲したい処理をwindow.postMessageとして使用する。

(MDNにはvar myWorker = new Worker('worker.js');のようにしてmyWorker.postMessageをするような感じになっているが...)
https://developer.mozilla.org/ja/docs/Web/API/Web_Workers_API/Using_web_workers

shin_k_2281shin_k_2281

Service Worker


(→https://ugo.tokyo/about-service-worker/から借りました)

Service Workerとはバックグラウンドで動くJavaScriptのことであり、オフラインで動作させるために
さまざまなタイミングにおいてServiceWorkerからイベントを発火させることができ、これらタイミングでそれぞれ処理を実行させることができる。
WEBブラウザとバックエンドサーバーの間に位置するプロキシのようなもの。

self.addEventListener('install', () => {
  console.log('install');
});
// 他にもactivateやidle, fetchなどがある

また、このようなコードを書くことによってスクリプトをService Workerとして登録する。

navigator.serviceWorker.register("/service-worker.js")

使用するモチベーション

  • ネイティブの技術を用いずに, installさせることなくアプリケーションをネイティブに近づけたい
    • スマートフォンのホーム画面へのアイコン追加 / オフラインキャッシュ / プッシュ通知 ...etc

使用上の注意

  • httpsかlocalhostのみで使用可能

参考

https://qiita.com/poster-keisuke/items/00056b8d5d6275afdb1a
https://numb86-tech.hatenablog.com/entry/2021/05/17/085336
https://ugo.tokyo/about-service-worker/

shin_k_2281shin_k_2281

PrefetchとPreloadの違いについて

chunkしたモジュールのローディングを最適化(事前に読み込んでおく)する機能。
(webpackでは、import() 関数に対して関数の中でコメントをつけるだけでできるとのこと。)
そうすることによって<link rel="prefetch" href="hoge.js">のようなタグが最終的に生成される。
またrefetchはナビゲーションの前に動作するのに対して、 Prelaod はナビゲーションが行われた後に動作する。

webpackにおけるprefetch/preloadや例えばSWRのpreloadなどなど用語としてたくさん出てくるがそれぞれで文脈が違いそうなのでそこまで深く考えないようにしておく...