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 を切るような場合もあった。
この程度の速度差であれば体感は難しいので、もはや誤差といえる。
しかし、これは逆に、省略がパースを著しく遅くする、といった影響はない と言えるだろう。
これなら、ペイロードが小さくなることによる、ネットワーク効率としてのメリットを重視するリスクはなさそうだ。
(もちろんサイズが大きくなれば変わるかもしれないが、このエントリはこのサイトの平均的なものなので問題ない)
同様の検証が他にもあったので貼っておく。結論はほぼ同じだった。
方法
普通の Web アプリでも、haml のように抽象度が高いフォーマットであれば、生成時に同様のオプションがあっても良さそうだが、haml ではずっと TODO のままのようだ。
Google の PageSpeed でも、こうした最適化は サポートされていた。
ただ、一般にあまり普及した方法とはいえない気がする。
Outro
HTML も省略可能な部分が有り、省略しても Valid な HTML にすることが可能である。
また、このエントリにおいては、省略によるパースへの影響は認められなかった。
HTML を手書きするのではなく、テンプレートなどから生成する場合、もし省略できるのなら、「片手間にスクレイピングしようとしている人にとって面倒」というあたり以外には、特にデメリットは無いように思う。
今回削除した以外にも、インデントや改行も最適化の対象ではあるが、本サイトのソースはあくまで観賞用であるためそのままにしている。