created_at
updated_at
tags
toc

HTML の省略によるサイズ最適化

Intro

本サイト blog.jxck.io 以下については、Markdown から静的ファイルを生成するスタイルで作成している。

この変換時に以前から思っていた HTML の最適化 を実施することにした。

しかし、md->html 変換時にそれをできるツールが見当たらないため、Markdown の AST から HTML を構築する過程で、省略を施すスクリプトを自作した。

ただし、ソースはあくまで観賞用なので、インデントやコメントは残している。

チューニングではなく単なる実験としてサイト全体にこれを適用し、その結果を記す。

静的コンテンツの最適化

パフォーマンス改善の常套手段として、コンテンツの最適化がある。

ただ、ここ最適化と言っているものには大きく二つある。

  • gzip などのアルゴリズムにより経路上で圧縮する
  • bmp を png, jpeg, webp などのフォーマットに変換し圧縮する
  • js や css から不要なスペースや改行を消すことで、コンテンツサイズを減らす

いずれも王道であり、ツールなどが充実しているため、特に導入は難しくないと思う。

ここで最後の最適化の一環として、HTML を最適化したことはあるだろうか?

あるとしても、それはスペース削除だけではないだろうか?

HTML の省略記法

タグそのもの

<html><head><body> は、タグ自体を書かなくても良い場合があり、仕様に定義がある。

An html element's end tag may be omitted if the html element is not immediately followed by a comment.

https://html.spec.whatwg.org/multipage/syntax.html#syntax-tag-omission

すぐ次がコメントでないなら、<html> は省略しても良い。<head><body> も同様だ。

<!-- before -->
<!DOCTYPE html>
  <head>
    <title>Hello</title>
  </head>
  <body>
    <p>Welcome to this example.</p>
  </body>
</html>

<!-- after -->
<!DOCTYPE html>
  <title>Hello</title>
  <p>Welcome to this example.</p>
</html>

これは、<html ng-app> みたいに、タグの要素によって何かを指定する必要があると消せないため、消せない/消しにくいことは多い。

閉じタグ

HTML には閉じタグの省略が許されているものがいくつかあり、仕様に定義がある。

例えば、</li> は以下の条件なら省略が可能だ。

An li element's end tag may be omitted if the li element is immediately followed by another li element or if there is no more content in the parent element.

https://html.spec.whatwg.org/multipage/syntax.html#syntax-tag-omission

  • その次にすぐ次の <li> がくる
  • それ以上親 (<ul>, <ol>) に子要素が無い

つまりこう書くことができる。

<!-- before -->
<ul>
  <li>one</li>
  <li>two</li>
  <li>three</li>
</ul>

<!-- after -->
<ul>
  <li>one
  <li>two
  <li>three
</ul>

単純に閉じタグ 5byte x 3 = 15byte 分のサイズが減っていることになる。

attribute value の quote

もう一つ、要素を囲む引用符(', ") も省略可能な場合があり、仕様では以下に定義がある。

The attribute value can remain unquoted if it doesn't contain space characters or any of " ' ` = < or >. Otherwise, it has to be quoted using either single or double quotes.

https://html.spec.whatwg.org/multipage/introduction.html#a-quick-introduction-to-html

つまり、スペースや ", ', `, =, <, > が無ければ引用符はいらない。

<!-- before -->
<input name="address" maxlength="200">

<!-- after -->
<input name=address maxlength=200>

要素一つにつき 2 byte の省略になる。

その最適化は現実的か?

好き嫌い

まず、こうした省略によって、例えば「XML として崩れている」みたいなことを言う人もいる。

気持ちは分かるが、そもそもこの程度を省略しなかったところで XML などではない。

あくまで書いているのは HTML であり、Markdown からの変換過程で最適化しているので、昨今のフロントのビルドタスクを見れば特段特別なことでもない。

影響

生成した結果は HTML Validator で確認しているが問題は無い。

HTML として問題が無いのであれば、HTML に対応したツールでは扱えることになる。使えない場合、少し厳しく言うとそのツールは HTML を正しく扱えてない、つまりバグということになる。

まあ、そもそも HTML というのは、XML と全く違い「緩い」部分が多いため、パースするのは非常に難しいので、完全に扱いきれないツールがあっても多少は仕方がない。

特に片手間な正規表現で HTML を処理している場合(スクレイピング、エディタのシンタックスハイライト etc)は、影響が皆無ともいえないが、現状自分の環境で、特に困った場面はいまのところない。

それも、強気に言えば「そのライブラリ etc が正しく HTML を扱えてないだけだ」と言うこともできなくはない。

結局 HTML をパースしたければ、正しくパースできるライブラリを使うしかないという話になる。

効果

今回は正直やってみたかったからやっただけで、パフォーマンス的な効果というより、「それによってパースが遅くなったり、他に影響が出たりするのか?」が知りたかったというモチベーションがある。

なので、その視点からいくつかの測定を実施する。

まず、省略時のサイズであるが、この記事自体を <html>, <head>, <body> 、閉じたタグ、引用符を全て付けた場合と比べると 259 byte の削減になっていた。

$ wc html-compression.html
 131     408    9158 html-compression.html
$ wc html-no-compression.html
 141     417    9417 html-no-compression.html

また Chrome の devtools で二つのファイルをレンダリングし、Timeline から Parse HTML の時間を確認した。

10 回平均した結果がこうだ。

省略有
約 80ms
省略無
約 90ms

また、何回かやると 有 が 100ms を超える場合や 無 が 70ms を切るような場合もあった。

この程度の速度差であれば体感は難しいので、もはや誤差といえる。

しかし、これは逆に、省略がパースを著しく遅くする、といった影響はない と言えるだろう。

これなら、ペイロードが小さくなることによる、ネットワーク効率としてのメリットを重視するリスクはなさそうだ。

(もちろんサイズが大きくなれば変わるかもしれないが、このエントリはこのサイトの平均的なものなので問題ない)

同様の検証が他にもあったので貼っておく。結論はほぼ同じだった。

「HTML のタグは一部省略可能」表示速度はどちらが早いのか調べてみた

方法

普通の Web アプリでも、haml のように抽象度が高いフォーマットであれば、生成時に同様のオプションがあっても良さそうだが、haml ではずっと TODO のままのようだ。

Google の PageSpeed でも、こうした最適化は サポートされていた

ただ、一般にあまり普及した方法とはいえない気がする。

Outro

HTML も省略可能な部分が有り、省略しても Valid な HTML にすることが可能である。

また、このエントリにおいては、省略によるパースへの影響は認められなかった。

HTML を手書きするのではなく、テンプレートなどから生成する場合、もし省略できるのなら、「片手間にスクレイピングしようとしている人にとって面倒」というあたり以外には、特にデメリットは無いように思う。

今回削除した以外にも、インデントや改行も最適化の対象ではあるが、本サイトのソースはあくまで観賞用であるためそのままにしている。