ブログ

本番環境最適化:OXC Minifierでコードを圧縮

OXC Minifierを使ってJavaScriptコードを圧縮し、ファイルサイズを削減してページ読み込み速度を向上させる方法を学びます。Terserより5倍以上高速。

LibDoc Team 2026年3月10日 OXC 連載 63 分で読める
#oxc #minifier #terser #コード圧縮 #パフォーマンス最適化 #rust

本番環境最適化:OXC Minifierでコードを圧縮

Web開発では、1KBごとが重要です。より小さなJavaScriptファイルは、より速い読み込み速度、より少ない帯域幅消費、より良いユーザー体験を意味します。

今日は、OXC Minifier を学びます — Terserより5倍以上速いコード圧縮ツールです。

なぜコード圧縮が必要なのか?

数字で見る

プロジェクトのバンドル後のJSファイルを想定:

状態ファイルサイズ4Gネット読み込み時間3Gネット読み込み時間
未圧縮500 KB1.2秒4秒
圧縮後180 KB0.4秒1.5秒

圧縮で64%のサイズ削減! これはモバイルユーザーにとって特に重要です。

コード圧縮は何をするのか?

シンプルな例を見てみましょう:

// オリジナルコード(約200バイト)
function calculateTotal(items) {
  let total = 0;
  for (const item of items) {
    if (item.price > 0) {
      total = total + item.price * item.quantity;
    }
  }
  return total;
}

export { calculateTotal };

圧縮後:

// 圧縮後(約80バイト)
function calculateTotal(e){let t=0;for(const o of e)o.price>0&&(t+=o.price*o.quantity);return t}export{calculateTotal};

圧縮の具体的な操作

操作説明
空白の削除スペース、改行、インデントを削除
コメントの削除すべてのコメント内容を削除
変数名の短縮長い変数名を短い名前に変更
定数のマージ変わらない値を統合
式の簡素化論理式を最適化
デッドコードの削除到達不可能なコードを削除

Minifierで何ができる?

OXC Minifierは以下の機能を提供します:

  • コード圧縮:ファイルサイズを削減
  • コード難読化:コードを読みにくくする(リバースエンジニアリングの難易度を上げる)
  • デッドコード削除:未使用のコードを削除
  • Source Map生成:デバッグ能力を保持

クイックスタート

インストール

npm install @oxc-minifier

基本的な使用方法

import { minifySync } from "@oxc-minifier";

const code = `
function greeting(name) {
  const message = "Hello, " + name + "!";
  console.log(message);
  return message;
}

export { greeting };
`;

const result = minifySync(code);

console.log(result.code);

出力:

function greeting(e){const o="Hello, "+e+"!";return console.log(o),o}export{greeting};

非同期圧縮

import { minify } from "@oxc-minifier";

const result = await minify(code, {
  compress: true,
  mangle: true,
});

console.log(result.code);

実践:プロジェクトを圧縮

コード圧縮の流れを一通り演练しましょう。

ステップ1:テストプロジェクトの作成

mkdir minify-demo
cd minify-demo
npm init -y
npm install @oxc-minifier
mkdir src dist

src/bundle.js を作成:

// src/bundle.js
/**
 * ユーザー管理モジュール
 * @module userManager
 */

// 設定
const API_BASE_URL = "https://api.example.com/v1";
const DEFAULT_TIMEOUT = 5000;
const MAX_RETRIES = 3;

/**
 * ユーザークラス
 */
class User {
  constructor(id, name, email) {
    this.id = id;
    this.name = name;
    this.email = email;
    this.createdAt = new Date();
  }

  getDisplayName() {
    return `${this.name} <${this.email}>`;
  }

  toJSON() {
    return {
      id: this.id,
      name: this.name,
      email: this.email,
      createdAt: this.createdAt.toISOString(),
    };
  }
}

/**
 * ユーザーサービス
 */
class UserService {
  constructor(baseUrl = API_BASE_URL) {
    this.baseUrl = baseUrl;
    this.cache = new Map();
  }

  async fetchUser(userId) {
    // キャッシュをチェック
    if (this.cache.has(userId)) {
      return this.cache.get(userId);
    }

    // リクエスト送信
    const response = await fetch(`${this.baseUrl}/users/${userId}`, {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
      },
    });

    if (!response.ok) {
      throw new Error(`Failed to fetch user: ${response.status}`);
    }

    const data = await response.json();
    const user = new User(data.id, data.name, data.email);

    // 結果をキャッシュ
    this.cache.set(userId, user);

    return user;
  }

  async updateUser(userId, updates) {
    const response = await fetch(`${this.baseUrl}/users/${userId}`, {
      method: "PATCH",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(updates),
    });

    if (!response.ok) {
      throw new Error(`Failed to update user: ${response.status}`);
    }

    // キャッシュをクリア
    this.cache.delete(userId);

    return this.fetchUser(userId);
  }
}

// デフォルトインスタンスを作成
const userService = new UserService();

// エクスポート
export { User, UserService, userService, API_BASE_URL };

ステップ2:圧縮スクリプトの作成

minify.js を作成:

// minify.js
import { minifySync } from "@oxc-minifier";
import fs from "fs";

function minifyFile(inputPath, outputPath) {
  console.log(`📦 圧縮中: ${inputPath}`);

  // ソースファイルを読み込む
  const code = fs.readFileSync(inputPath, "utf-8");
  const originalSize = Buffer.byteLength(code, "utf-8");

  // 圧縮
  const result = minifySync(code, {
    compress: true,      // 圧縮を有効化
    mangle: true,        // 変数名難読化を有効化
    sourcemap: true,     // Source Mapを生成
  });

  // 圧縮後のコードを書き込む
  fs.writeFileSync(outputPath, result.code);

  // Source Mapを書き込む
  if (result.map) {
    fs.writeFileSync(`${outputPath}.map`, result.map);
  }

  const minifiedSize = Buffer.byteLength(result.code, "utf-8");
  const reduction = ((1 - minifiedSize / originalSize) * 100).toFixed(1);

  console.log(`✅ 出力: ${outputPath}`);
  console.log(`📊 サイズ: ${originalSize} → ${minifiedSize} バイト (${reduction}% 削減)`);
  console.log();
}

// 圧縮を実行
minifyFile("./src/bundle.js", "./dist/bundle.min.js");

ステップ3:圧縮の実行

node minify.js

出力:

📦 圧縮中: ./src/bundle.js
✅ 出力: ./dist/bundle.min.js
📊 サイズ: 2048 → 687 バイト (66.5% 削減)

ステップ4:圧縮結果の確認

dist/bundle.min.js の内容:

const t="https://api.example.com/v1",e=5e3;class s{constructor(t,e,s){this.id=t,this.name=e,this.email=s,this.createdAt=new Date}getDisplayName(){return`${this.name} <${this.email}>`}toJSON(){return{id:this.id,name:this.name,email:this.email,createdAt:this.createdAt.toISOString()}}}class i{constructor(e=t){this.baseUrl=e,this.cache=new Map}async fetchUser(t){if(this.cache.has(t))return this.cache.get(t);const e=await fetch(`${this.baseUrl}/users/${t}`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!e.ok)throw new Error(`Failed to fetch user: ${e.status}`);const i=await e.json(),r=new s(i.id,i.name,i.email);return this.cache.set(t,r),r}async updateUser(t,e){const s=await fetch(`${this.baseUrl}/users/${t}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!s.ok)throw new Error(`Failed to update user: ${s.status}`);return this.cache.delete(t),this.fetchUser(t)}}const r=new i;export{s as User,i as UserService,r as userService,t as API_BASE_URL};
//# sourceMappingURL=bundle.min.js.map

設定オプション詳細

compress - 圧縮オプション

minifySync(code, {
  compress: {
    // ブールオプション
    booleans: true,       // ブール値を最適化
    conditionals: true,   // 条件式を最適化
    dead_code: true,      // デッドコードを削除
    drop_console: false,  // console.* を削除
    drop_debugger: true,  // debugger 文を削除
    evaluate: true,       // 定数式を計算
    loops: true,          // ループを最適化
    unused: true,         // 未使用コードを削除

    // 数値オプション
    sequences: true,      // カンマ演算子で文をマージ
    comparisons: true,    // 比較演算を最適化
    inline: 2,            // シンプルな関数をインライン化
    passes: 2,            // 圧縮のパス数
  },
});

mangle - 難読化オプション

minifySync(code, {
  mangle: {
    toplevel: false,      // トップレベル変数を難読化するか
    properties: false,    // プロパティ名を難読化するか
    reserved: ["exports", "require"],  // 保持する名前

    // マッピングテーブルを出力(デバッグ用)
    // output_map: "mangle-map.json",
  },
});

format - フォーマットオプション

minifySync(code, {
  format: {
    comments: false,      // すべてのコメントを削除
    beautify: false,      // 出力を整形するか(デバッグ用)
    indent_level: 2,      // インデントレベル(整形時のみ有効)
  },
});

sourcemap - Source Map

// 外部Source Mapを生成
minifySync(code, {
  sourcemap: true,
});

// インラインSource Mapを生成
minifySync(code, {
  sourcemap: "inline",
});

// Source Mapを生成しない
minifySync(code, {
  sourcemap: false,
});

バンドルツールとの統合

Vite 統合

// vite.config.js
import { defineConfig } from "vite";

export default defineConfig({
  build: {
    minify: "oxc",
    // OXC 圧縮オプション
    oxc: {
      minify: {
        compress: true,
        mangle: true,
      },
    },
  },
});

Webpack 統合

// webpack.config.js
const OxcMinifyPlugin = require("@oxc-minifier/webpack");

module.exports = {
  mode: "production",
  optimization: {
    minimizer: [
      new OxcMinifyPlugin({
        compress: true,
        mangle: true,
        sourcemap: true,
      }),
    ],
  },
};

Rollup 統合

// rollup.config.js
import { oxcMinify } from "@oxc-minifier/rollup";

export default {
  input: "src/index.js",
  output: {
    file: "dist/bundle.js",
    format: "esm",
    sourcemap: true,
  },
  plugins: [oxcMinify()],
};

パフォーマンス比較

テスト環境

  • ファイル:中規模プロジェクトのバンドルファイル
  • オリジナルサイズ:1.2 MB
  • マシン:MacBook Pro M1

Terserとの比較

// benchmark.js
import { minifySync as oxcMinify } from "@oxc-minifier";
import { minify as terserMinify } from "terser";
import fs from "fs";

const code = fs.readFileSync("./large-bundle.js", "utf-8");

// OXC Minifier
console.time("OXC Minifier");
for (let i = 0; i < 5; i++) {
  oxcMinify(code, { compress: true, mangle: true });
}
console.timeEnd("OXC Minifier");

// Terser
console.time("Terser");
for (let i = 0; i < 5; i++) {
  terserMinify(code, {
    compress: true,
    mangle: true,
  });
}
console.timeEnd("Terser");

テスト結果

ツール圧縮時間出力サイズ圧縮率
Terser3.2秒420 KB65%
OXC Minifier0.5秒435 KB63.75%

OXC MinifierはTerserより約6倍速い!

圧縮率はわずかに低いですが、速度の優位性は明らかで、大規模プロジェクトではより大きなメリットがあります。

圧縮前後の比較

例1:条件式の最適化

// オリジナルコード
function getStatus(user) {
  if (user.isActive === true) {
    if (user.hasPremium === true) {
      return "premium";
    } else {
      return "active";
    }
  } else {
    return "inactive";
  }
}
// 圧縮後
function getStatus(e){return e.isActive?!0===e.hasPremium?"premium":"active":"inactive"}

例2:定数畳み込み

// オリジナルコード
const SECONDS_PER_MINUTE = 60;
const MINUTES_PER_HOUR = 60;
const SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;

function formatDuration(seconds) {
  const hours = Math.floor(seconds / SECONDS_PER_HOUR);
  const remainingSeconds = seconds % SECONDS_PER_HOUR;
  const minutes = Math.floor(remainingSeconds / SECONDS_PER_MINUTE);
  return `${hours}h ${minutes}m`;
}
// 圧縮後(定数は計算済み)
function formatDuration(e){const s=Math.floor(e/3600);return`${s}h ${Math.floor(e%3600/60)}m`}

例3:デッドコード削除

// オリジナルコード
function debug(message) {
  if (process.env.NODE_ENV === "development") {
    console.log("[DEBUG]", message);
  }
}

function doSomething() {
  debug("Starting operation");
  return "done";
}

// 本番環境では、debug関数の呼び出しは削除される
// 圧縮後(NODE_ENV=productionと仮定)
function doSomething(){return"done"}

よくある質問

圧縮後のコードでエラーが発生?

考えられる原因:

  1. eval または with の使用:これらは変数スコープに影響し、圧縮でエラーが発生する可能性
  2. 特定のプロパティ名への依存:コードがプロパティ名文字列に依存している場合、プロパティを難読化しない

解決方法:

minifySync(code, {
  mangle: {
    reserved: ["importantName"],  // 特定の名前を保持
  },
});

Source Mapが一致しない?

圧縮オプションがバンドルツールの設定と一致していることを確認:

// Source Mapを有効にする
minifySync(code, {
  sourcemap: true,
});

特定のコメントを保持したい?

minifySync(code, {
  format: {
    comments: /license|copyright/i,  // これらのキーワードを含むコメントを保持
  },
});

consoleを削除せずログを保持?

minifySync(code, {
  compress: {
    drop_console: true,  // すべての console.* を削除
  },
});

ベストプラクティス

1. 本番環境で圧縮を有効化

{
  "scripts": {
    "build": "vite build --mode production",
    "build:dev": "vite build --mode development"
  }
}

2. デバッグ用にSource Mapを保持

// 本番環境でもSource Mapを生成(エラー監視プラットフォームにアップロード)
minifySync(code, {
  sourcemap: true,
});

3. 圧縮と難読化を分離

// 圧縮のみ、難読化なし(デバッグ段階)
minifySync(code, {
  compress: true,
  mangle: false,
});

// 完全圧縮(リリース段階)
minifySync(code, {
  compress: true,
  mangle: true,
});

4. gzipとの組み合わせでさらに圧縮

# 圧縮後にgzip
oxc-minify bundle.js -o bundle.min.js
gzip -k bundle.min.js

# 最終ファイル:bundle.min.js.gz

まとめ

この記事ではOXC Minifierの核心的な使い方を紹介しました:

内容説明
コード圧縮ファイルサイズを60%以上削減
変数名難読化リバースエンジニアリングの難易度を上げる
Source Mapデバッグ能力を保持
パフォーマンスの優位性Terserより5倍以上速い

Minifierの核心価値:ユーザーのダウンロード量を減らし、読み込みを高速化。

次のステップ

Minifierの設定オプションをさらに知りたいですか?**OXC日本語ドキュメント - Minifierセクション**をご覧ください。

次回の記事では、OXC Resolver を学びます — モジュールパス解決の魔法を理解しましょう!


💡 関連記事