JavaScriptミニファイア

コメント、スペース、不要な文字を削除してJavaScriptコードを圧縮します。

JavaScriptミニファイについて

JavaScriptはほとんどのWebページで最も重いリソースタイプであり、それは最大のファイルである傾向があるからだけではありません。コストは5段階あります: ダウンロード(ネットワーク経由)、パース(エンジンがバイトを読み込みASTを構築)、コンパイル(V8またはJavaScriptCoreがASTをバイトコードにコンパイル)、実行(バイトコードが実行される)、そしてその後の訪問では、キャッシュされたバイトコードからのウォームスタートエンジンがそれを保持している場合。CDNエッジのBrotliはダウンロードコストを処理します。ミニフィケーションはダウンロードにも役立ちますが、スクリプトをダウンロードするすべてのデバイスでのパースとコンパイルのコストにも役立ちます。ローエンドのAndroid電話では、パースとコンパイルがネットワーク転送よりも時間がかかる可能性があります。Addy OsmaniのCost of JavaScript記事は、ネットワーク問題に見えるものはしばしばCPU問題であり、ミニフィケーションの20-40%のバイト削減が、遅いデバイスのロングテールでのパース時間からかなり直接的に削られたミリ秒に変換されることを繰り返し示してきました。

V8の遅延パース最適化は、実際に呼び出されるまで任意の関数の完全なパースを遅延させます。これは、使用されていない関数が多い大きなスクリプトのコストがそのバイト数が示唆するよりも少ないことを意味します。ChromeのHTTPキャッシュは、V8バイトコード(「Code Cache」)も保存するため、リピート訪問はパース・コンパイルステージを完全にスキップします。これらは基本的な方程式を変えるものではありません: 小さいスクリプトはウォームキャッシュをより速くヒットし、コールドパスでより速くパースし、より速くコンパイルします。ミニフィケーションは、ミリ秒を取り戻すためにスタック全体で最も安価な場所です。

JavaScriptミニフィケーションの4つ(または5つ)のレベル

すべてのミニファイアが同じではなく、必要なレベルはコードベースに依存します。レベル1: 空白とコメントの削除。 最も単純なパス、スペース、タブ、改行、// ... / /* ... */ コメントを取り除きます。これはregexパスが安全に行えること(文字列、テンプレートリテラル、regexリテラル周辺で注意して)です。典型的な削減: 生バイトで20-40%。レベル2: シンボルのリネーム(マングリング)。 ローカル変数名、関数パラメータ、(注意して)関数名が単一文字に書き直されます: function calculateTotal(itemList)function a(b) になります。これは、どの名前がリネームしても安全かを知るために抽象構文木と完全なスコープ分析を必要とします。グローバル名、エクスポート、this のプロパティへの参照、および文字列キーアクセス(obj['name'])で到達するものはすべてそのまま残す必要があります。典型的な追加削減: 10-25%。レベル3: デッドコード除去(ツリーシェイキング)。 モジュールレベルの静的分析は、決して実行されないインポートとコードパスを識別し、それらを削除します。モジュールレベルの型情報と副作用の明確な理解を必要とします。典型的な追加削減: 可変ですが、多くの機能を出荷するライブラリでは膨大になる可能性があります。レベル4: インライン化と定数畳み込み。 2 * 60 * 60 のような単純な式はビルド時に 7200 に評価されます; 一度だけ呼び出される小さな関数は呼び出し元にインライン化される可能性があります。レベル5: プロパティマングリング。 最も攻撃的な最適化、オブジェクトのプロパティ名も書き直されます。文字列キー(obj['name'] vs obj.name)を使用するコード、またはプロパティ名を公的契約の一部として公開するコードを壊します。ほとんど常にオプトインで、ほとんど常に --mangle-props regex経由で特定の識別子に制限されます。

JavaScriptミニフィケーションツールの簡単な歴史

JSMin (2003)。 Douglas Crockford、はい、JSONを推進したのと同じCrockfordが、2003年にCでJSMinを書きました。AST、スコープ分析なしで基本的な空白とコメントの削除を行う小さな単一ファイルプログラムで、ASI(Automatic Semicolon Insertion)のエッジケースに対して意図的に保守的なアプローチを取っていました。「動作する最も単純なもの」のバーを設定し、それ以来すべてのregexベースのJSミニファイアの精神的祖先です。YUI Compressor (2007)。 YahooのJulien Lecomteは2007年8月11日にYUI Compressorを発表しました。RhinoトークナイザーをANALYZER使用して安全なシンボルリネームを行い、JavaScriptに対して実際のASTベースのマングリングを行った最初の広く使用されたJavaツールでした。Closure Compiler (2009)。 Googleは2005年から内部的に使用していました; 2009年11月5日にApache 2.0の下でオープンソース化しました。Closureはその時代の最も攻撃的なオプティマイザーで、JSDocアノテーション経由で型対応で、型推論に基づいてプロパティ名を書き直すことができる「advanced」モードを持っていました。トレードオフは、コードをClosureフレンドリーな方法で書く必要があることでした; advancedモードの失敗は悪名高かったです。

UglifyJS (~2010-2012)。 Mihai BazonのUglifyJSは最初のJavaScriptネイティブミニファイアで、JSで書かれ、Node.js上で実行され、10年間npmのデフォルトになりました。UglifyJS 2はソースマップサポートとES5機能を追加しました; UglifyJS 3はES5の磨きを続けて続きましたが、ES6+サポートを完全に得ることはありませんでした。Terser (2018年8月)。 Fabricio Mattéは、長期間安定したUglifyJS APIを破壊せずにES6+サポートを追加するために、UglifyJSをTerserにフォークしました。Terserは現在、webpack 5、Rollup、Parcel 1、および古いNext.jsバージョンでデフォルトのJSミニファイアです。swc (2017/2019)。 Donny「kdy1」Choiのswc(「Speedy Web Compiler」)は、Terserより20-70倍速い組み込みミニファイアを持つRustベースのJavaScript/TypeScriptコンパイラです。Next.jsは2021年10月のバージョン12からデフォルトミニファイアをTerserからswcに切り替えました。esbuild (2019-2020年冬)。 Figmaの共同創業者Evan Wallaceは、2019-2020年冬休みにesbuildをリリースしました。Goで書かれており、当時のJavaScriptベースのバンドラーより10-100倍速く、独自のミニファイアを出荷します。esbuildは現在、Vite、tsup、および多くのフレームワークテンプレートの基盤となるミニファイアです。過去5年間の一般的な方向は: 高速システム言語(RustまたはGo)で書かれたパーサー、ASTベースの最適化、インテリジェントなESモジュール対応のツリーシェイキングです。このツールのようなブラウザに貼り付けるregexミニファイアは、そのはしごの一番下に位置し、まだ役立つ最も単純な仕事をしています。

ミニファイされたJavaScriptのソースマップ

ソースマップは、ミニファイされた出力の位置を元のソースの位置にマップするJSONサイドカーファイルです。Source Map V3仕様は、John Lenz (Google) と Nick Fitzgerald (Mozilla) によって2011年に書かれ、2024年6月にTC39によってECMA-426として採用されました。同じソースマップ形式がJavaScriptとCSSの両方に適用されます。ブラウザはミニファイされたファイル内の末尾のコメント経由でマップを消費します: //# sourceMappingURL=app.js.map。DevToolsが開いておりマップが取得されると、Sourcesパネルは元のソースを表示し、ブレークポイント、コンソールエラー、スタックトレースはすべてそれを参照します。プロダクションミニファイア(Terser、swc、esbuild、Closure)はすべてリクエストに応じてソースマップを発行します。このツールはそうしません、テキストではなくダウンロード可能なファイルペアを返すブラウザ内のワンショットツールの場合、ソースマップは限界的な利益のために重要な複雑さを追加します。正直な開示は、このツールは一方向のパスです; ビルドパイプラインミニファイアは、元のソースがディスク上にあり、開発者がランタイムでデバッグする必要があるため、ソースマップに対してはるかに強いケースを持っています。

モダンバンドラーのデフォルト、ほとんどの人はすでにミニファイアを持っている

モダンなビルドパイプラインを使用している場合、ミニファイアはすでに実行中です。webpack 5はデフォルトで terser-webpack-plugin をTerserと共に使用します。Viteはデフォルトでミニフィケーションにesbuildを使用します; CSS用にはLightning CSSです。Parcelはswcを使用します。Next.jsはv12(2021年10月)でTerserからswcに切り替え、同じバージョンで完全なビルドパイプラインのためBabelからswcに切り替えました。Remix、Astro、SvelteKit、Nuxt、Rollup、esbuildスタンドアロン、すべては開発者の介入なしにプロダクションビルドにミニフィケーションをバンドルします。結果は、モダンなビルドパイプラインを使用している人にとって、JSミニフィケーションは単一ファイルregexツールができることをはるかに超える最適化で自動的に行われることです。このブラウザ内ミニファイアが自分の価値を稼ぐケース: インライン <script> ブロックを含む手書きのHTMLページ; Nodeツールチェーンなしで出荷されるWordPressテーマ; ミニフィケーションをバンドルしない静的サイトジェネレーター; CMSまたはメールテンプレートに貼り付けられた一回限りのスニペット; ビルドパイプラインのセットアップにスクリプト自体より時間がかかる迅速な実験。

正直なスコープ: このツールができることとできないこと

このツールはregexベースのミニファイアで、約30行のJavaScriptです。文字列リテラル、テンプレートリテラル、regexリテラルをプレースホルダーにトークン化して、後続の変換が内容を破損できないようにします; // .../* ... */ コメントを取り除きます; 空白の連続を折りたたみます; 必要ない句読点周辺の空白を削除します; トークン化されたリテラルを復元します。典型的な出力は入力より生バイトで20-40%小さいです。このツールがしないこと、プロダクションミニファイア(Terser、swc、esbuild、Closure)が処理すること: ローカル変数を単一文字にリネームしない(スコープ対応マングリングなし); デッドコード除去またはツリーシェイキングを実行しない; 定数畳み込みまたは式の簡略化を実行しない; ソースマップを発行しない; TypeScript構文を理解しない(プレーンJavaScriptのみ貼り付け); ESモジュールインポートをツリーシェイクしない; プロパティ名を書き直しない。正直な枠組み: エディターまたは手から出てきたJavaScriptを貼り付け、典型的に生バイトで20-40%小さくstripされたバージョンを受け取り、クイックデプロイアーティファクトとして使用します。ビルドパイプラインを持つプロジェクトの場合、そのパイプラインでTerser、swcまたはesbuildを使用してください; AST対応の最適化は、このツールの20-40%の削減とプロダクションミニファイアの60-80%の違いです。

Regexミニファイアの落とし穴

JavaScript文法を理解しないregexベースのパスは、微妙な方法でコードを破損できます。古典的なトラップ: テンプレートリテラルはバックティックを使用し、regexが慎重でない場合にコメント取り除き候補に見える補間式(`Hello ${name}!`)を含むことができます。Regexリテラルとして /^\/\*/g はフォワードスラッシュ、コメントのようなシーケンス、文字列のような内容を含みます; それらを誤って処理すると、regexが構文的に壊れたコードになります。コメントのようなテキストを含む文字列(const url = "// example.com")、ナイーブなコメント削除は // 以降のすべてを取り除きます。ASIのgotcha、Automatic Semicolon InsertionはほとんどのときセミコロンをマンクできるJS機能ですが、次のトークンがパレン、ブラケット、または演算子((/regex/)[arr]+1)で始まるとき、空白の取り除きと悪く相互作用します。不注意な空白折りたたみは「2つの文」を「パースエラーを持つ1つの文」に変えることができます。このツールの軽減策は、最初に実行されるリテラルトークン化パスで、すべての文字列とregexをユニークプレースホルダーで置き換え、クリーンアップされたコードでコメント+空白の作業を行い、その後プレースホルダーを復元します。完璧ではありませんが、一般的なケースをカバーします。ミニファイアがコードを壊したと疑うなら、最初にチェックすべきは、トークナイザーが見逃したスラッシュシーケンスを含むregexリテラルです。

プライバシー: なぜブラウザのみがここで重要なのか

サーバーサイドJSミニファイア(コードをサーバーにPOSTし、そこでTerser経由で実行し、結果を返すオンラインツール)はソースのアップロードを必要とします。通常のライブラリコードの場合これは無害です。内部ツール、未リリースの製品コード、インラインAPIキーまたはサードパーティサービスクレデンシャルを含むJavaScript、または専有アルゴリズムまたはビジネスロジックを明らかにするコードの場合、そうではありません。純粋にブラウザベースのミニファイア、初期ページロード後にネットワークリクエストを行わないあなたのタブで実行されているJavaScriptは、問題を回避します。DevToolsのネットワークタブを開き、コードを貼り付け、Minifyをクリックし、送信リクエストを見ることで検証できます。さらに良いことに、ページがロードされた後にインターネットから切断する(または機内モードを有効にする)とツールはまだ機能します。これは何もアップロードされていないという最も強い実証的証拠です。

よくある質問

私のコードはどれくらい小さくなりますか?

コメントとインデントのある手フォーマットされたコードの場合、生バイトで20-40%のサイズ削減を期待してください。完全なASTベースの最適化を持つプロダクションミニファイア(Terser、swc、esbuild)は60-80%を達成します、ギャップはシンボルリネーム、デッドコード除去、定数畳み込みで、regexのみのツールが安全に行えるものはありません。CDNエッジでのBrotli圧縮の後、ミニフィケーションからの追加の節約はより控えめです(非ミニファイの元のものに対してBrotliを超えて5-15%)が、ゼロではなく、スケールでは積み重なります。

ミニファイされた出力は私のコードを壊しますか?

コードの大多数では、いいえ。既知のエッジケースは、スラッシュシーケンス(/foo\/bar/)を持つregexリテラル、行をまたぐ埋め込み補間を持つテンプレートリテラル、および改行を取り除くとパースが変わるAutomatic Semicolon Insertionコンテキスト周辺です。ツールのリテラルトークン化パスは一般的なケースを処理しますが、コードがそれらのパターンのいずれかで異常に重い場合は、デプロイ前にミニファイされた出力をブラウザでテストしてください。プロダクションビルドパイプラインの場合は、Terserまたはswcを使用してください、それらは完全なAST認識を持ち、これらのケースを正しく処理します。

このツールは変数をリネームしますか?

いいえ。変数マングリング、function calculateTotal(itemList)function a(b) に変換することは、どの名前がリネームしても安全かを知るためにAST上の完全なスコープ分析を必要とします。regexパスはこれを安全に行えません。シンボルリネームには、ビルドパイプラインでTerser、UglifyJSまたはswcを使用してください; それらはスコープ対応マングリングを正しく実装します。それが生成する10-25%の追加サイズ削減は実際で、プロダクションコードに対して行う価値があります。

TypeScriptを貼り付けることができますか?

いいえ、このツールはJavaScriptのみのミニファイアです。TypeScriptは型アノテーション(function add(a: number, b: number): number)、インターフェース、enum、デコレーター、および有効なJavaScriptではない他の構文を追加します。最初に tsc、swc、esbuildまたはBabelを使用してTypeScriptをJavaScriptにコンパイルし、ここにJavaScriptの出力を貼り付けてください。ほとんどのTypeScriptプロジェクトは、すでにそれらのコンパイラのいずれかをビルドの一部として通っているので、JavaScriptはどこかに存在します。

すでにビルドパイプラインを持っている場合、これを使うべきですか?

おそらくいいえ、バンドラーがあなたのためにこれを行っており、regexツールが提供できるよりはるかに進んだASTベースの最適化を行っています。webpack 5はTerserと共に terser-webpack-pluginを出荷します; Viteはデフォルトでesbuildを使用してJSします; Parcelはswcを使用します; Next.jsはv12(2021年10月)以来swcを使用しています。このツールは、ビルドパイプラインがカバーしないケースのためのものです: 手書きのHTMLページ、Nodeツールチェーンなしで出荷されるWordPressテーマ、ミニフィケーションをバンドルしない静的サイトジェネレーター、一回限りのスニペット、またはビルドのセットアップにスクリプト自体より時間がかかる迅速な実験。

私のファイルはアップロードされますか?

いいえ。ミニファイアはあなたのブラウザで実行されているJavaScriptです。貼り付けたコードは決してネットワークを越えません、Minifyをクリックする間にDevToolsのネットワークタブで検証するか、ロード後にページをオフラインに(機内モードに)してもツールはまだ機能することを確認してください。内部ツール、未リリースの製品コード、インラインAPIキーまたは専有ビジネスロジックを含むスクリプトは、あなたのデバイスに留まります。

関連ツール