FlowingDev

JavaScriptのMinifyとBeautify入門:コードを整えるか、削ぎ落とすか

JavaScriptのMinify(圧縮)がコードを縮小してウェブサイトを高速化する仕組みと、Beautify(整形)が人間にとって読みやすいコードにする方法を解説します。

ツールを試す: JS / TS ツール

一文で言うと

JavaScriptのMinifyは、マシンがより速くダウンロードできるようにコードを圧縮するもので、一方Beautify(またはPretty-Printing)は、人間が読みやすいようにフォーマットを整えるものです。

どんな問題を解決するのか

ウェブの石器時代、JavaScriptファイルはGeoCitiesのページに雪を降らせるための、ちっちゃなスクリプトでした。我々開発者はコードを書き、保存し、それで終わり。自分たちとブラウザのためにコードを書いていて、両者は同じものでした。

その後、「Web 2.0」革命がやってきます。Gmail、Googleマップ、Facebookは、ウェブページが本格的なアプリケーションになりうることを示しました。これは、JavaScriptがもはや雪を降らせるだけのものではなく、複雑なロジックを処理し、データを取得し、ページの広大な領域を操作するためのものになったことを意味します。私たちのスクリプトファイルは数キロバイトから数百、そして数千キロバイトへと膨れ上がりました。

これが根本的な対立を生み出しました:

  • 人間には読みやすいコードが必要。 私たちはスペース、タブ、改行、説明的な変数名(totalOrderAmountIncludingTaxとか)、そしてコメントを使って、コードを保守しやすく、デバッグしやすく、チームメイトが理解しやすいようにします。
  • ブラウザには小さなコードが必要。 すべてのスペース、すべての改行、変数名のすべての余分な文字は、ネットワークを介して転送しなければならないもう1バイトになります。低速なモバイル接続のユーザーにとって、美しく読みやすいコードで満たされた1MBのJavaScriptファイルは、待たなければならない1MBのファイルです。ブラウザのJavaScriptエンジンは、あなたの変数がxと名付けられていようがaVeryDescriptiveAndHelpfulVariableNameと名付けられていようが、まったく気にしません。ただロジックを実行するだけです。

ここでMinifyとBeautifyの出番です。これらは同じコインの裏表であり、人間が読めるソースコードの世界と、ネットワークに最適化されたマシンコードの世界との間の翻訳者として機能します。Minifyは、アプリケーションが重厚になった現代のウェブを可能にした、不可欠な「本番環境向けのコンパイル」ステップとなりました。Beautifyは、その本番コードで一体何が起こっているのかを解明しようとする開発者にとって、不可欠な「難読化解除」ステップとなったのです。

内部の仕組み

これらのツールがやっているのは、ただの気の利いたテキストの検索・置換だと思われるかもしれません。いいえ、違います!コードを安全に変換するためには、コードを理解しなければなりません。このプロセスは、本格的なコンパイラがやることをシンプルにしたようなものです。

基礎:抽象構文木(AST)

ツールがコードをMinifyまたはBeautifyする前に、まずそれを抽象構文木(AST: Abstract Syntax Tree)と呼ばれるデータ構造にパース(解析)する必要があります。これが絶対的なキーポイントです。ASTは、空白やコメントのような「ふわっとした」部分をすべて無視して、コードの文法構造を木で表現したものです。

  1. 字句解析(トークン化): パーサーはまず生のテキストをスキャンし、言語の最小単位である「トークン」のストリームに分解します。let a = 10;の場合、トークンはlet、a、=、10、;になります。
  2. 構文解析(パース): 次に、ツールはこのトークンのストリームを受け取り、コードの関係性を表す木構造に配置します。

const num = 42;のような単純な一行の場合、ASTは次のようになります:

- VariableDeclaration (kind: 'const')
  - VariableDeclarator
    - id: Identifier (name: 'num')
    - init: Literal (value: 42)

一度コードがこの木構造になれば、コードの変換は木を操作し、変更された木から新しいコード文字列を生成するだけの問題になります。

Minify:圧縮プレイ

Minifyは、元のコードと機能的に同等な、可能な限り最小のコードを作成するために設計された非可逆的な処理です。ASTに対していくつかの方法で作用します:

1. 空白、改行、コメントの削除 これは最も簡単な効果です。ASTは本質的でない空白やコメントを表現しないため、生のASTからコードを生成するだけで、それらは自動的に取り除かれます。

// Before
// calculates the final price
const price = 100;
const tax   = 20;

let finalPrice = price + tax;

// After AST -> String
const price=100;const tax=20;let finalPrice=price+tax;

2. 識別子の名前変更(mangle) これが大きな節約につながる部分です。MinifierはASTをたどり、すべての変数宣言と関数宣言を見つけ、それらを可能な限り短い名前(a、b、t、nなど)に変更します。スコープを賢く理解しているので、ある関数内のeという変数が、別の関数内のeと衝突することはありません。

// Before
function calculateTotal(items, discountPercentage) {
  let subTotal = 0;
  for (const item of items) {
    subTotal += item.price;
  }
  return subTotal * (1 - discountPercentage / 100);
}

// After mangling
function a(t,e){let n=0;for(const o of t){n+=o.price}return n*(1-e/100)}

itemがoに、itemsがtに、discountPercentageがeに、subTotalがnになったことに注目してください。

3. 式の単純化 最も高度なMinifierは、ミニコンパイラのように振る舞い、ロジックを最適化します。ASTを変換して、よりコンパクトな構文を使用します。

  • if (debug === true) { console.log('hi') } は debug&&console.log("hi") になるかもしれません。
  • x = new Array(1, 2, 3) は x=[1,2,3] になります。
  • true は !0 に、false は !1 になります。

Beautify:ふわっとした部分を元に戻す

Beautify、またはPretty-Printingは、その逆のプロセスです。コード(多くの場合、Minifyされて醜い)を受け取り、それを読みやすくします。

これもまた、コードをASTにパースすることから始まります。このステップにより、入力コードにフォーマットが一切なくても機能することが保証されます。

次に、ASTをたどってコード文字列を再生成しますが、今度は事前に定義された一連のスタイルルールに従います。スタイルガイドを持ったロボットのようなものだと考えてください:

  • 「VariableDeclarationノードを見たら、const またはlet を出力する」
  • 「+や=のような二項演算子を見たら、その前後にスペースを出力する」
  • 「BlockStatement({...}内のコード)に入るときは、インデントレベルを1つ上げる」
  • 「文を終える;を見たら、改行文字を出力する」
// Minified Input
function a(t,e){let n=0;for(const o of t){n+=o.price}return n*(1-e/100)}

// Beautified Output
function a(t, e) {
  let n = 0;
  for (const o of t) {
    n += o.price;
  }
  return n * (1 - e / 100);
}

重要なのは、BeautifyはMinifyの過程で破壊された情報を復元することはできないということです。元の変数名(calculateTotalなど)やコメントは永遠に失われてしまいます。Beautifierにできる最善のことは、名前が変更されたロジックを構造的に読みやすくすることだけです。

実話から学ぶ

動作の重いECサイトのチェックアウト事件

あるスタートアップが、ピカピカの新しいECサイトを立ち上げました。すべて順調に見えましたが、分析データを見ると、特にモバイルユーザーのチェックアウトページでの離脱率が非常に高いことがわかりました。ページは重く感じられ、インタラクティブになるまでに時間がかかりすぎていました。ある開発者がブラウザのネットワークタブを開くと、犯人が見つかりました。1.2MBもある単一のcheckout.jsファイルです。それは、開発者のコメント、空白、そして美しく長い変数名が詰まった、生の、Minifyされていないソースコードでした。彼らはデプロイパイプラインにMinifyのステップを追加しました。するとcheckout.jsファイルは450KBに縮小。翌日、ページの読み込み時間は半分に短縮され、チェックアウトのコンバージョン率は上昇し始めました。

教訓: Minifyは「あったらいいね」的な最適化ではありません。優れたユーザー体験のための基本的な要件であり、ビジネス目標に直接影響します。

謎のサードパーティ製ウィジェット事件

マーケティングチームが開発者に、「今話題の」顧客フィードバックウィジェットをウェブサイトに追加してほしいと依頼しました。ベンダーからは、HTMLに貼り付けるための一行のJavaScriptが提供されました。開発者がそれを貼り付けると、突然、特定のページでサイトのメインナビゲーションメニューが壊れ始めました。ベンダーのコードは、解読不能な8000文字のMinifyされたごちゃごちゃした一行でした。イライラした開発者は、その行全体をコピーしてBeautifierに貼り付けました。するとコードは即座に、読みやすい(まだ謎めいてはいるが)構造に花開きました。整形されたコードを読むことで、彼女はロジックをたどり、問題を発見しました。そのウィジェットが、サイト自身のメニュースクリプトが依存している一般的なグローバル変数を不注意に再定義していたのです。この知識をもとに、彼女はウィジェットのコードを分離し、衝突を防ぐための簡単な修正を書くことができました。

教訓: Beautifierは、元のソースがないサードパーティ製コードや本番コードを調査、デバッグし、安全にやり取りするための、あなたの秘密の解読機です。

終わらないコードレビュー事件

チームのジュニア開発者が、最初の大きな機能を提出しました。コードは完璧に動作しましたが、フォーマットがめちゃくちゃでした。あるファイルはタブを使い、別のファイルはスペースを使っている。中括弧の位置は一貫性がありませんでした。関数宣言があるときは一行に押し込められ、またあるときは5行にわたって広がっていました。シニア開発者のコードレビューは、「ここにスペースを追加して」「このブロックをインデントしてください」といった何十ものコメントで真っ赤に染まりました。コードの実際のロジックは、そのノイズの中に埋もれてしまいました。うんざりしたシニア開発者は、彼らのワークフローに自動整形ツール(PrettierのようなBeautifier)を導入しました。それ以降、すべてのコードは保存時に自動的に整形されるようになりました。コードレビューは即座に生産的になり、スタイルの些細な指摘ではなく、アーキテクチャやロジックに集中できるようになったのです。

教訓: チーム全体でBeautifyを自動化することで、無意味な議論をなくし、一貫性を強制し、開発者が本当に重要なこと、つまり良いコードを書くことに集中できるようになります。

よくある間違いと落とし穴

  • ソースマップを忘れること。 これが最大の落とし穴です。本番環境用にコードをMinifyするときは、「ソースマップ」ファイルも生成すべきです。このファイルは、小さく難読化された本番コードと、あなたの美しくオリジナルのソースコードとの間の地図です。本番環境でエラーが発生したとき、ブラウザの開発者ツールはソースマップを使って、Minifyされたごちゃごちゃのコードではなく、あなたのオリジナルコードでエラー箇所を示してくれます。ソースマップの生成やアップロードを忘れると、本番環境のデバッグが生き地獄になります。
  • MinifyされたファイルをGitにコミットすること。 やってはいけません。Minifyされたファイルは「ビルド成果物」です。つまり、開発プロセスのソースではなく出力です。リポジトリを肥大化させ、マージを不可能にし、意味のない差分(diff)を生み出します。ビルドパイプライン(例:Vite, Webpack)が、本番ビルドのためにオンデマンドで生成すべきものです。
  • Beautifyがソースを復元すると信じること。 Beautifierはコードを読みやすくできますが、Minifierが最適化して消し去った元の変数名、コメント、ロジック構造を元に戻すことはできません。それはデバッグの補助ツールであり、タイムマシンではありません。
  • 過度にアグレッシブなMinify設定でコードが壊れること。 いくつかの高度なMinify設定は、あなたのコードについて必ずしも安全とは言えない仮定をすることがあります。これは特に、コードが動的なプロパティアクセス(例:window['my' + 'Func']())を使用していたり、関数のnameプロパティに依存している場合に当てはまります。Minifyステップの後に、必ずアプリケーションを徹底的にテストしてください。前だけではダメです。

なぜ注目すべきか

ワークフローの3つの重要なタイミングで、MinifyとBeautifyについて考えてみてください:

  1. 書いているとき: コードエディタに統合されたPrettierのようなBeautifier/フォーマッタを使いましょう。「保存時にフォーマット」するよう設定してください。これにより、あなたとあなたのチームのコードスタイルの一貫性の問題が永遠に解決されます。
  2. デプロイするとき: Minifyは、本番ビルドプロセスにおける自動的で、譲れない必須のステップであるべきです。実際のユーザーに使われるウェブアプリケーションを構築しているなら、JavaScript、CSS、HTMLをMinifyしなければなりません。
  3. デバッグするとき: ライブサイト(自分のか他人ののかを問わず)のコードを調査したり、サードパーティのスクリプトを分析したりする必要がある瞬間、Beautifierはあなたが最初に手を伸ばすべきツールです。マシンに最適化されたコードを、人間が分析を開始できる何かに変えてくれます。

もっと深く

  • Wikipedia: Minification (programming) - 概念とその歴史についての優れた概観。
  • AST Explorer - JavaScriptコードがどのように抽象構文木にパースされるかを見ることができる、素晴らしいインタラクティブツール。
  • Terser Documentation - 最も人気でパワフルなJavaScript Minifierの一つであるTerserのウェブサイト。そのドキュメントは、高度な最適化オプションに関する深い洞察を与えてくれます。
  • Prettier: The Opinionated Code Formatter - コード整形におけるデファクトスタンダードであるPrettierのホームページ。その哲学が説明されています。
  • Source Map Revision 3 Spec - ソースマップが内部でどのように機能するかについての、詳細な技術仕様。

理論はOK。さあ手を動かそう — 100%ブラウザ内で。

ツールを試す: JS / TS ツール