JavaScript ファイルの圧縮・再訪

JavaScript ファイルの圧縮と一口にいってもおおまかに次の3種類があります。

  • コンテンツの圧縮(gzip);ブラウザ機能による伸長
  • コンテンツの圧縮(compress);JavaScript による伸長
  • コンテンツの縮小(minify)

それぞれについて説明します。

コンテンツの圧縮;ブラウザ機能による伸長

ブラウザが圧縮コンテンツをサポートしている場合,リクエストヘッダの Accept-Encoding に gzip(や deflate)という値を渡します。この場合,サーバは圧縮されたコンテンツを渡すことができます。

一番簡単で安全なのは mod_deflate を使うことです*1。基本動作として*2リクエストヘッダの Accept-Encoding に gzip が指定されている場合のみ,モジュールが圧縮して伝送してくれます。

フィルタモジュールなので,CGIPHP)等による動的生成コンテンツに圧縮もかけられます。

ただし,デメリットもあります。

私見では今時これくらいはデメリットに入らないかと思います。


これらのデメリットが受容できないか,mod_deflate なんてうちのサーバに入ってないよって場合,静的コンテンツであれば,あらかじめ gzip で圧縮して置いておくこともできます。この際,適切な Content-Type と Content-Encoding を返してやることが望ましいです。

たとえば,JavaScript ファイル foo.js を gzip 圧縮して foo.jsz というファイル名にした場合,

# .htaccess

AddType application/x-javascript jsz
AddEncoding gzip jsz

みたくすればいいです。

このままだと,ブラウザが gzip 伸長に対応していることが前提になってしまいます。たとえば jQuery とかバリバリ使ってモダンブラウザしか相手にしないぜ〜ってことであれば静的 gzip 圧縮でもいいかも。

それ以外に対応するためには mod_rewrite 等を利用して,非対応ブラウザでは非圧縮コンテンツを返すように設定する必要があります(参考⇒prototype.jsを10KBにする方法の続き(.htaccessをスマートに使う) | 亜細亜ノ蛾)。わたし的には mod_rewrite を使うくらいなら mod_deflate 使えよと思いますが,世の中には mod_rewrite ならインストールされてるんだけど,というサーバも少なくないかも。

ちなみに Web で調べて見たところ Safari についてちょっと情報が錯綜してますが,

  • Safari 1.x は Accept-Encoding を送らない。gzip 圧縮された JavaScript ファイルにも対応してないっぽい。
  • Safari 2.x は Accept-Encoding を送る。ので,gzip 圧縮された JavaScript ファイルにも対応している。

というのが私の試した結果です。

コンテンツの圧縮(compress);JavaScript による伸長

これについてはかつて書きました(⇒JavaScript の圧縮 - daily dayflower)ので詳しく説明しません。

静的辞書法等でファイルを圧縮しておき,ブラウザに読み込まれたときに JavaScript で展開する,というものです。

デメリットとしては,

  • 一見難読化されているようだけど,元のコードに簡単にもどせるのでその部分は期待できない
  • ファイルを読み込んだ後に JavaScript で展開するため,クライアントサイドで実際の処理を開始するまでにラグが発生・負荷が上昇する。

があります。ので,今日日流行っていません。

コンテンツの縮小(minify)

今アツイのがこれ。

  • 基本機能: コメントの削除
  • 基本機能: 余分な空白,改行,セミコロンの削除
  • 応用機能: ローカル変数名の短縮・難読化

どのような実装があるのかというと,

など,いろんな言語であります。

前者2つはいずれも Rhino*3をパーサとして利用しているので,文法的に不要なものを厳密に判断しやすい…つまり圧縮率が高いです。その代わり,重いためオンライン圧縮(リアルタイム圧縮)には向いていません。

後者2つは…きちんとコードを読んでないのですが,正規表現等による簡易パーサを利用しているので改行等不要な部分が結構残ってしまうらしいです。また上であげたローカル変数名等については原理上対応しにくいです*4。その代わり安全性が高く(ロジックが変わりにくい),軽快なためオンライン圧縮に向いています。

YUI Compressor 作者の Julien Lecomte 氏によると,Dojo Compressor はローカル変数名の短縮をやりすぎるのでやや危険,YUI Compressor は minify と同じくらい安全だよ,とのことですが(⇒Introducing the YUI Compressor | Julien Lecomte's Blog),まあ本人の言なのでなんとも。


で,ロジックからすると静的辞書法などに比べると断然圧縮率は落ちるのですが,なぜアツイかというと,これと最初にあげた gzip 圧縮を絡めることができて,そうすると圧縮率を補えるからです。

File File size Gzipped file size
Original jQuery library 62,885 bytes 19,758 bytes
jQuery minified with JSMin 36,391 bytes 11,541 bytes
jQuery minified with Packer 21,557 bytes 11,119 bytes
jQuery minified with the YUI Compressor 31,822 bytes 10,818 bytes
http://www.julienlecomte.net/blog/2007/08/21/gzip-your-minified-javascript-files/

まあ YUI Compressor + Gzip が一番小さくなったのは結果的にそうなったというだけでアレなんですが,

  • 元々 63KB あった jQuery (1.1系列と思われる)が
  • gzip 非対応ブラウザでも 32KB に
  • gzip 対応ブラウザだと 11KB に!

という風にとらえることができると思います。

もちろん世の中がすべて gzip 対応ブラウザだと何もしなくても 20KB で済むのですが,余計なコメント等がないと,ブラウザ側の JavaScript パーサもやや処理が軽くなるんじゃないかという期待もあります。

まとめ

  • minifing + mod_deflate だとみんな幸せ


おまけ。mod_deflate がないけど mod_rewrite を使ってあれこれするのも…という向きには,自分で gzip 圧縮するスクリプトを書くという手もあります。たとえば⇒ http://www.julienlecomte.net/blogfiles/gz.phps


あと,圧縮とは関係ないですが,複数の小さな JavaScript ファイルを読み込むより,まとめて一つのファイルにしたほうが転送は早い気がします。たとえ Keep-Alive でも。Firebug の Net タブを見ていてそう思いました。

追記 2007/09/21

2007年09月21日 miya2000 javascript mod_deflate って毎回圧縮するんでしょうか?

http://b.hatena.ne.jp/miya2000/20070921#bookmark-5944766

基本的に毎回圧縮します。

もちろん,サーバ側 Expires ヘッダやクライアント側 If-Modified-Since ヘッダの絡みでリクエストが発生しない場合がありますが,サーバにリクエストが届けば都度都度圧縮します(そういえば ETag はどうなってるんだろう)。キャッシュ的な機能はありません。

上にも書いたように,巨大なサイトを運営しているのでなければ気になるほどのサーバ負荷ではないと思ってます(実験したわけではなくてただの予想)。もし気になるようであれば,静的なファイルに限った話ですが相当な未来の Expires ヘッダを吐くようにして,js ファイル名にタイムスタンプを加えるとか,「<script src="hogehoge.js?1190265347" ...」みたくすればいいんじゃないかと思います。動的コンテンツじゃないと面倒ですが。

*1:適切な設定が必要ですが

*2:SetEnvIf 等である種の環境変数を指定することでこのへんの挙動は変わります⇒[http: //httpd.apache.org/docs/2.2/mod/mod_deflate.html:title]

*3:Java による JavaScript 実装

*4:packer のように独自記法を導入すれば不可能ではないと思いますが