Rspackバンドル最適化実践ガイド


Rspackは、Rustで書かれた高速なWebバンドラーとして、Webpackとの互換性を保ちながら圧倒的なビルド速度を実現します。本記事では、Rspackを使った実践的なバンドル最適化手法について解説します。

Rspackの基本セットアップ

まず、Rspackの基本的なセットアップから始めましょう。

npm install -D @rspack/cli @rspack/core

基本的な設定ファイル rspack.config.js を作成します。

/** @type {import('@rspack/cli').Configuration} */
module.exports = {
  entry: './src/index.ts',
  output: {
    filename: '[name].[contenthash].js',
    path: __dirname + '/dist',
    clean: true,
  },
  optimization: {
    minimize: true,
  },
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: 'builtin:swc-loader',
        type: 'javascript/auto',
      },
    ],
  },
};

Tree Shakingの最適化

1. sideEffectsの設定

package.json でside effectsを明示的に指定することで、Tree Shakingを最適化できます。

{
  "name": "my-app",
  "sideEffects": false
}

特定のファイルにside effectsがある場合は配列で指定します。

{
  "sideEffects": [
    "*.css",
    "*.scss",
    "./src/polyfills.ts"
  ]
}

2. ESモジュールの使用

Tree Shakingを最大限活用するため、CommonJSではなくESモジュールを使用します。

// ❌ CommonJS
const { someFunction } = require('./utils');

// ✅ ESモジュール
import { someFunction } from './utils';

3. Named Exportsの活用

default exportよりもnamed exportsを使用することで、Tree Shakingがより効果的に機能します。

// utils.ts
// ✅ Named exports
export function add(a: number, b: number) {
  return a + b;
}

export function subtract(a: number, b: number) {
  return a - b;
}

export function multiply(a: number, b: number) {
  return a * b;
}

// ❌ Default export with object
export default {
  add,
  subtract,
  multiply,
};

使用側では必要な関数のみをインポート。

// ✅ 必要な関数のみインポート
import { add, subtract } from './utils';

console.log(add(1, 2));
console.log(subtract(5, 3));

Code Splittingの実装

1. Dynamic Importによる遅延ロード

動的インポートを使用して、必要なときにのみコードを読み込みます。

// ❌ 静的インポート
import HeavyComponent from './HeavyComponent';

// ✅ 動的インポート
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <HeavyComponent />
    </Suspense>
  );
}

2. SplitChunksPluginの設定

Rspackの optimization.splitChunks を設定して、共通モジュールを分割します。

module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        // ベンダーコードを分離
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          priority: 10,
        },
        // Reactライブラリを分離
        react: {
          test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
          name: 'react',
          priority: 20,
        },
        // 共通コードを分離
        common: {
          minChunks: 2,
          priority: 5,
          reuseExistingChunk: true,
        },
      },
    },
    runtimeChunk: {
      name: 'runtime',
    },
  },
};

3. ルートベースのコード分割

ルーティングごとにバンドルを分割することで、初期ロードを高速化します。

// routes.tsx
import { lazy } from 'react';

const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Dashboard = lazy(() => import('./pages/Dashboard'));

export const routes = [
  { path: '/', element: <Home /> },
  { path: '/about', element: <About /> },
  { path: '/dashboard', element: <Dashboard /> },
];

SWC設定の最適化

Rspackは内部的にSWCを使用しているため、SWCの設定を最適化することでビルドを高速化できます。

1. SWCローダーの設定

module.exports = {
  module: {
    rules: [
      {
        test: /\.(js|ts|jsx|tsx)$/,
        use: {
          loader: 'builtin:swc-loader',
          options: {
            jsc: {
              parser: {
                syntax: 'typescript',
                tsx: true,
                decorators: true,
              },
              transform: {
                react: {
                  runtime: 'automatic',
                  development: process.env.NODE_ENV === 'development',
                },
              },
              minify: {
                compress: true,
                mangle: true,
              },
            },
          },
        },
        type: 'javascript/auto',
      },
    ],
  },
};

2. ターゲットブラウザの指定

.browserslistrc でターゲットブラウザを指定します。

> 0.5%
last 2 versions
not dead
not IE 11

または、rspack.config.js で直接指定。

module.exports = {
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: {
          loader: 'builtin:swc-loader',
          options: {
            env: {
              targets: {
                chrome: '90',
                firefox: '88',
                safari: '14',
                edge: '90',
              },
            },
          },
        },
      },
    ],
  },
};

3. 開発環境と本番環境の分離

// rspack.config.js
const isDevelopment = process.env.NODE_ENV === 'development';

module.exports = {
  mode: isDevelopment ? 'development' : 'production',
  devtool: isDevelopment ? 'cheap-module-source-map' : 'source-map',
  optimization: {
    minimize: !isDevelopment,
    minimizer: [
      new rspack.SwcJsMinimizerRspackPlugin({
        compress: {
          passes: 2,
          drop_console: !isDevelopment,
        },
        mangle: true,
      }),
    ],
  },
};

Webpack互換性

RspackはWebpackの主要なAPIと互換性を持っています。既存のWebpackプロジェクトからの移行を容易にします。

1. Webpackローダーの使用

多くのWebpackローダーはRspackでも動作します。

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader', 'postcss-loader'],
      },
      {
        test: /\.scss$/,
        use: ['style-loader', 'css-loader', 'sass-loader'],
      },
    ],
  },
};

2. Webpackプラグインの互換性

一部のWebpackプラグインはRspackでも動作します。

const HtmlWebpackPlugin = require('html-webpack-plugin');
const { DefinePlugin } = require('@rspack/core');

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
    }),
    new DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
    }),
  ],
};

3. aliasとresolveの設定

Webpackと同様のalias設定が可能です。

const path = require('path');

module.exports = {
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
      '@components': path.resolve(__dirname, 'src/components'),
      '@utils': path.resolve(__dirname, 'src/utils'),
    },
    extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
  },
};

パフォーマンス計測とモニタリング

1. バンドルサイズの分析

npm install -D @rspack/plugin-bundle-analyzer
const { BundleAnalyzerPlugin } = require('@rspack/plugin-bundle-analyzer');

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin({
      analyzerMode: 'static',
      reportFilename: 'bundle-report.html',
    }),
  ],
};

2. ビルド時間の計測

const start = Date.now();

module.exports = {
  plugins: [
    {
      apply(compiler) {
        compiler.hooks.done.tap('BuildTimePlugin', () => {
          const end = Date.now();
          console.log(`Build completed in ${((end - start) / 1000).toFixed(2)}s`);
        });
      },
    },
  ],
};

3. Chunk命名の最適化

module.exports = {
  output: {
    filename: '[name].[contenthash:8].js',
    chunkFilename: '[name].[contenthash:8].chunk.js',
  },
  optimization: {
    moduleIds: 'deterministic',
    chunkIds: 'deterministic',
  },
};

実践的な最適化戦略

1. プリロードとプリフェッチ

// 重要なチャンクをプリロード
import(/* webpackPreload: true */ './CriticalComponent');

// 将来使用する可能性のあるチャンクをプリフェッチ
import(/* webpackPrefetch: true */ './OptionalComponent');

2. 環境変数の最適化

const { DefinePlugin } = require('@rspack/core');

module.exports = {
  plugins: [
    new DefinePlugin({
      __DEV__: JSON.stringify(process.env.NODE_ENV === 'development'),
      __API_URL__: JSON.stringify(process.env.API_URL),
    }),
  ],
};

3. CSSの最適化

npm install -D css-minimizer-rspack-plugin
const CssMinimizerPlugin = require('css-minimizer-rspack-plugin');

module.exports = {
  optimization: {
    minimizer: [
      new CssMinimizerPlugin({
        minimizerOptions: {
          preset: ['default', { discardComments: { removeAll: true } }],
        },
      }),
    ],
  },
};

まとめ

Rspackを使ったバンドル最適化では、以下のポイントが重要です。

  • Tree Shaking: sideEffectsの設定とESモジュールの使用
  • Code Splitting: 動的インポートとsplitChunksの最適化
  • SWC設定: ターゲットブラウザと環境に応じた最適化
  • Webpack互換性: 既存のローダーとプラグインの活用
  • モニタリング: バンドルサイズとビルド時間の継続的な計測

Rspackは高速性とWebpack互換性を両立しており、既存プロジェクトの段階的な移行も可能です。適切な最適化戦略により、ビルド時間の大幅な短縮とバンドルサイズの削減を実現できます。