【実務・中級編】 HTMLパーサーのトークン化プロセス – Webブラウザの仕組み実践ガイド

こんにちは。君たちのチームに参加して、ブラウザの内部挙動について熱く語るシニアアーキテクトの席をもらったよ。

普段、何気なく書いているHTML。`

`タグを閉じ忘れたり、不審な階層でマークアップしたりしても、現代のブラウザは文句一つ言わずにいい感じに画面を描画してくれるよね。「ブラウザって賢いな〜」なんて思っていないかい?

実は、あの裏側で何が行われているかを知ることは、中級からワンランク上のシニアエンジニアへステップアップするための必須教養なんだ。「なぜこの書き方をするとレンダリングが遅くなるのか」「なぜDOMの構造が意図せず崩れるのか」。その全ての答えは、今回深掘りするHTMLパーサーの「トークン化プロセス(字句解析)」に隠されている。

さあ、ブラウザの頭脳の最深部へ潜ってみようか。

—

1. なぜブラウザはHTMLを「そのまま」理解できないのか?

僕たちが書いたHTMLファイルは、ただの「ただの文字の羅列(バイト列 / 文字列)」にすぎない。CPUやブラウザのエンジン(BlinkやWebKitなど)にとって、`

Hello

` という文字列は、意味を持たない単なる文字の並びだ。

これをブラウザが理解できる構造(DOMツリー)に変換する最初の関門が、字句解析(Lexical Analysis)、すなわちトークン化(Tokenization)のプロセスだ。

トークン化って一体なんだ?

例えるなら、日本語の文章を「名詞」「動詞」「助詞」という最小単位のパーツ(形態素)に分解する作業に似ている。HTMLパーサーは、ソースコードを左から右へ1文字ずつ(あるいはチャンク単位で)スキャンし、意味のある最小単位の塊である「トークン(Token)」へと切り分けていくんだ。

HTML5の仕様(WHATWG)では、このトークン化のアルゴリズムが「状態機械(State Machine)」として厳密に定義されている。ブラウザの内部では、今「タグの中にいるのか」「テキストの中にいるのか」「コメントアウトの中にいるのか」という「状態(State)」を常に遷移させながら、高速に文字を削り取っている。

—

2. トークン化の裏側:状態機械(State Machine)の魔術

HTMLパーサーの心臓部は、「トークナイザー(Tokenizer)」と呼ばれるステートマシンだ。
文字を1文字ずつ読み込みながら、状態を切り替えていく。主なトークンの種類を見てみよう。

1. StartTag(開始タグトークン): `

` のようなもの。タグ名と属性のリストを持つ。
2. EndTag(終了タグトークン): `

` のようなもの。
3. Character(文字トークン): タグに挟まれたテキストノードの中身。
4. Comment(コメントトークン): `` など。
5. EOF(End of File): ソースコードの終端。

ライブ感のある具体例:パーサーの頭の中を覗く

例えば、以下のシンプルなHTMLがあったとする。

Hello World

ブラウザのパーサーがこれをどう読み解くか、その足取りを追ってみよう。

1. `<` に遭遇すると、パーサーは「データ状態」から「タグ名開始状態(Tag open state)」へ遷移する。
2. 次の文字 `p` を読み、「タグ名状態(Tag name state)」に入り、`p` という文字を蓄積する。
3. `>` に遭遇すると、タグが閉じられたと判断し、`StartTag: p` というトークンを発行する。
4. 次の文字群は通常のテキストなので、「データ状態(Data state)」に戻り、`Hello ` という`Character` トークンを連続して発行する。
5. 再び `<` が来たら、同様に `StartTag: strong` を作り、テキストを挟んで、最終的に `EndTag: strong`、`EndTag: p` を次々と生成していく。

この生み出されたトークンのストリームが、次の工程である「ツリー構築(Tree Construction)」フェーズへ流し込まれ、ようやくお馴染みのDOM(Document Object Model)ツリーへと組み上げられるわけだ。

—

3. 実務で役立つ!HTMLパースの挙動をハックするTips

「トークン化の仕組みなんて、ブラウザが勝手にやってくれるんだから知らなくてもいいじゃん」と思ったそこの君。大間違いだ。この仕組みを知っているだけで、実務でのパフォーマンスチューニングやバグ回避の視点劇的に変わる。

Tips 1: 「閉じタグの省略」がパーサーに与える負荷

HTML5では、`

` や `

` などの終了タグを省略できる仕様になっている。例えば以下のコードを見てほしい。

  • リスト1
  • リスト2
  • リスト3

ブラウザのパーサーは、次の `

  • ` や `

    ` に遭遇した瞬間、「あ、前の `

  • ` がまだ閉じてないから、ここで自動的に `EndTag` トークンを仮想的に挿入して閉じよう」と判断する。
    これ自体は仕様通りなんだけど、パーサーに余計な「状態の推測と仮想トークンの生成」というオーバーヘッドを強いることになる。 大規模なアプリケーションであればあるほど、こうした小さな省略の積み重ねがパース処理のボトルネック(Main Threadのブロック)になり得る。コードは正しく閉じ、パーサーを迷わせないのがプロの流儀だ。

    Tips 2: `document.write` やインラインスクリプトが描画を止める理由

    DOM構築中に `

  • frontendintronationalをフォローする

    コメント

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