やあ。今日もどこかのコードレビューで「なんでこんなところに`script`タグ置いてるんだよ……」と頭を抱えているところかい?
フロントエンドをやっていると、画面が真っ白なまま数秒間固まる「ホワイトスクリーン問題」に一度は直面するはずだ。ユーザーが離脱する魔の数秒間。その犯人の多くは、DOM構築の裏側でこっそり悪さをしているJavaScriptのブロッキング挙動にある。
今回は、ブラウザがHTMLをどう読み、なぜJSがパースを止めてしまうのか、そしてそれをどうスマートに回避するのかについて、現場の知見を交えて徹底的に解説していこう。
—
1. HTMLパースの裏側:ブラウザのメインスレッドは超多忙
まず大前提として、Webブラウザのメインスレッドは基本的にシングルスレッドで動いている。HTMLのパース、CSSOMの構築、JavaScriptの実行、レイアウト計算、ペイント(描画)……これらすべてを、あの限られたシングルスレッドの上で時分割で処理しているんだ。
ブラウザがサーバーからHTMLを受け取ると、ネットワーク層からストリーミングされてくるバイトデータを「HTMLパーサー」に流し込む。このパースの基本方針は「インクリメンタル(逐次処理)」だ。つまり、HTMLの先頭から順に文字を読み込み、トークナイザーがタグを認識した瞬間に、上から下へ向かってDOM(Document Object Model)ツリーをコツコツと組み立てていく。
[HTMLバイトデータ]
↓
[トークナイザー]
↓
[DOMツリー構築] ← ここにインクリメンタルにノードが追加されていく
ユーザービリティの観点から言えば、このツリーが少しずつ構築されるにつれて、ブラウザは画面をチラ見せ(漸進的レンダリング)させたいわけだ。しかし、ここに「あいつ」が割り込んでくると、すべてが台無しになる。そう、`