【実務・中級編】 JavaScriptの実行タイミング制御(defer, async, type=module) – Webブラウザの仕組み実践ガイド

やあ、お疲れ様。
最近、チームのコードレビューをしていて「とりあえず `async` つけとけば速くなるっしょ」的なノリで `script` タグを書いてるコードを見かけてね、少しブラウザの裏側の動きを整理しておいた方がいいなと思ったんだ。

フロントエンドの中級から一段上のシニアへステップアップするにあたって、「ブラウザがHTMLをどう読み、どう解釈し、どこでJavaScriptをねじ込んでいるのか」というレンダリングパイプラインの全体像を正確に理解しておくことは、パフォーマンスチューニングの武器として絶対に欠かせない。

今回は、HTMLパースをブロックせずにJSを走らせるための魔術である `defer`、`async`、そして現代の標準である `type=”module”` の挙動について、ブラウザの裏側の事情を交えながら徹底的に紐解いていこうか。

—

1. 前提:なぜ素の `` は悪者扱いされるのか?

まず大前提として、ブラウザがHTMLファイルを受け取ってから画面を表示するまでの流れを思い出してほしい。

ブラウザのパーサー(HTML Parser)は、ネットワーク経由で送られてきたバイトストリームを文字に直し、トークンに分解し、DOM(Document Object Model)ツリーを上から順に組み立てていく。この真っ最中に、HTMLの中に普通の `` を書いた場合、デフォルトでその挙動は自動的に `defer` になる。

明示的に `defer` と書かなくても、ブラウザはモジュールスクリプトをHTMLパースと並行してダウンロードし、DOMの構築がすべて終わってから(かつ依存関係のグラフを解決した上で)実行してくれる。

さらに、モジュールスクリプトに `async` を付与することも可能だ。
`


パターンB:依存関係のないサードパーティ製スクリプト(`async` の活用)

ページ内の他の要素に依存せず、かつ他のスクリプトを待たせる必要がないトラッキングコードや計測ツールの場合。





アナリティクス連携の例




メインコンテンツ


パターンC:古いブラウザ(IEなど、もう絶滅危惧種だが…)へのフォールバック

もし、まだレガシーな環境をサポートする必要がある場合、`nomodule` 属性を組み合わせることで、モダンブラウザとレガシーブラウザで読み込むスクリプトを綺麗に切り分けることができる。





モダン/レガシーの切り分け






---

5. チーフアーキテクトからの実践的なアドバイス

最後に、実務でパフォーマンスチューニングを行う際に見落としがちな罠を一つ伝えておこう。

「じゃあ、全部のスクリプトに `async` や `defer` をつければ完璧だな!」と短絡的に考えてはいけない。
特に `async` を不適切なスクリプト(例えば、他の自作ライブラリ関数に依存しているスクリプトなど)に付けてしまうと、「たまに再現する、タイミング依存の謎の `TypeError: Cannot read properties of undefined`」という、デバッグ泣かせの亡霊を生み出すことになる。

スクリプトの依存関係を頭の中でしっかりマッピングし、
1. メインのアプリロジック: `type="module"`(実質 `defer`)で安全に、順序を守って動かす。
2. 独立した外部ツール(アナリティクスや広告): `async` で一刻も早く非同期で読み込ませる。

この基本原則をチーム全員が共通認識として持てば、Webページの初期表示速度(Core Web Vitalsの指標、特にINPやLCP)は劇的に改善されるはずだ。

明日からのコードレビューで、「お、このscriptタグ、ちゃんと特性を理解して選んでるな」と後輩のコードにニヤリとできることを期待しているよ。さて、次のタスクに取り掛かろうか。

コメント

タイトルとURLをコピーしました