Rspack完全ガイド — Rust製の超高速バンドラーでWebpack互換の次世代ビルド体験
Rspackは、Rust言語で実装されたWebpack互換の次世代JavaScriptバンドラーです。Webpackのエコシステムと互換性を保ちながら、Rustによる10倍以上の高速化を実現し、大規模プロジェクトのビルド時間を劇的に短縮します。この記事では、Rspackの基本から実践的な使い方まで徹底的に解説します。
Rspackとは
Rspackは、ByteDance(TikTokを運営する企業)が開発したRust製の高速バンドラーです。Webpackの設計思想を受け継ぎながら、パフォーマンスを大幅に向上させています。
主な特徴
- 超高速ビルド - Rustによる実装で、Webpackの10倍以上の速度
- Webpack互換 - 既存のWebpack設定やプラグインの多くがそのまま動作
- Hot Module Replacement (HMR) - 高速なHMRで開発体験が向上
- Tree Shaking - 最適化された未使用コードの削除
- Code Splitting - 効率的なコード分割
- ゼロコンフィグ - 初期設定なしで即座に利用可能
- 生産性向上 - ビルド時間短縮により開発サイクルが高速化
なぜRspackなのか
Webpack vs Rspack (10,000ファイルのプロジェクト)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
初回ビルド:
Webpack: 120秒
Rspack: 8秒 ← 15倍高速
再ビルド (HMR):
Webpack: 3秒
Rspack: 0.1秒 ← 30倍高速
プロダクションビルド:
Webpack: 180秒
Rspack: 12秒 ← 15倍高速
インストールとセットアップ
新規プロジェクトの作成
# Rspackプロジェクトを作成
npm create rspack@latest my-app
cd my-app
# テンプレートを選択
# - react
# - react-ts
# - vue
# - vue-ts
# - vanilla
# - vanilla-ts
npm install
npm run dev
既存プロジェクトへの追加
npm install -D @rspack/core @rspack/cli
基本的な設定ファイル
// rspack.config.js
const rspack = require('@rspack/core');
/**
* @type {import('@rspack/cli').Configuration}
*/
module.exports = {
entry: './src/index.js',
output: {
path: __dirname + '/dist',
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.css$/,
use: ['css-loader'],
type: 'css',
},
],
},
plugins: [
new rspack.HtmlRspackPlugin({
template: './src/index.html',
}),
],
};
package.jsonのスクリプト設定
{
"scripts": {
"dev": "rspack serve",
"build": "rspack build",
"build:prod": "rspack build --mode production"
}
}
Reactプロジェクトの設定
TypeScript + React設定
// rspack.config.js
const rspack = require('@rspack/core');
const refreshPlugin = require('@rspack/plugin-react-refresh');
const isDev = process.env.NODE_ENV === 'development';
/**
* @type {import('@rspack/cli').Configuration}
*/
module.exports = {
entry: {
main: './src/index.tsx',
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
},
module: {
rules: [
{
test: /\.tsx?$/,
use: {
loader: 'builtin:swc-loader',
options: {
jsc: {
parser: {
syntax: 'typescript',
tsx: true,
},
transform: {
react: {
runtime: 'automatic',
development: isDev,
refresh: isDev,
},
},
},
},
},
type: 'javascript/auto',
},
{
test: /\.css$/,
use: ['css-loader', 'postcss-loader'],
type: 'css',
},
{
test: /\.module\.css$/,
use: [
{
loader: 'css-loader',
options: {
modules: true,
},
},
'postcss-loader',
],
type: 'css/module',
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
],
},
plugins: [
new rspack.HtmlRspackPlugin({
template: './index.html',
}),
isDev && new refreshPlugin(),
].filter(Boolean),
optimization: {
minimize: !isDev,
},
devServer: {
port: 3000,
hot: true,
historyApiFallback: true,
},
};
CSS Modules対応
// CSS Modulesの設定
module.exports = {
module: {
rules: [
{
test: /\.module\.css$/,
use: [
{
loader: 'css-loader',
options: {
modules: {
localIdentName: '[name]__[local]--[hash:base64:5]',
},
},
},
],
type: 'css/module',
},
],
},
};
使用例:
// Button.tsx
import styles from './Button.module.css';
export const Button = () => {
return <button className={styles.button}>Click me</button>;
};
/* Button.module.css */
.button {
padding: 10px 20px;
background: blue;
color: white;
border: none;
border-radius: 4px;
}
Vueプロジェクトの設定
// rspack.config.js
const rspack = require('@rspack/core');
const { VueLoaderPlugin } = require('vue-loader');
/**
* @type {import('@rspack/cli').Configuration}
*/
module.exports = {
entry: './src/main.ts',
resolve: {
extensions: ['.ts', '.js', '.vue', '.json'],
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
},
{
test: /\.ts$/,
use: {
loader: 'builtin:swc-loader',
options: {
jsc: {
parser: {
syntax: 'typescript',
},
},
},
},
type: 'javascript/auto',
},
{
test: /\.css$/,
use: ['vue-style-loader', 'css-loader'],
},
],
},
plugins: [
new VueLoaderPlugin(),
new rspack.HtmlRspackPlugin({
template: './index.html',
}),
],
};
WebpackからRspackへの移行
移行ステップ
# 1. Rspackのインストール
npm install -D @rspack/core @rspack/cli
# 2. Webpackの削除(任意)
npm uninstall webpack webpack-cli webpack-dev-server
# 3. 設定ファイルのリネーム
mv webpack.config.js rspack.config.js
# 4. package.jsonのスクリプト更新
# webpack → rspack に変更
設定の変換
// Before: webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: './src/index.js',
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
}),
new MiniCssExtractPlugin(),
],
};
// After: rspack.config.js
const rspack = require('@rspack/core');
module.exports = {
entry: './src/index.js',
plugins: [
new rspack.HtmlRspackPlugin({
template: './src/index.html',
}),
// CSSはビルトインでサポート
],
module: {
rules: [
{
test: /\.css$/,
type: 'css', // Rspackのビルトイン機能
},
],
},
};
互換性のあるWebpackプラグイン
多くのWebpackプラグインがRspackで動作します:
// 動作するWebpackプラグイン
const { DefinePlugin } = require('@rspack/core');
const CopyPlugin = require('copy-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const CompressionPlugin = require('compression-webpack-plugin');
module.exports = {
plugins: [
new DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
}),
new CopyPlugin({
patterns: [{ from: 'public', to: 'dist' }],
}),
new BundleAnalyzerPlugin(),
new CompressionPlugin(),
],
};
ローダーの設定
SWCローダー(ビルトイン)
module.exports = {
module: {
rules: [
{
test: /\.(js|jsx|ts|tsx)$/,
use: {
loader: 'builtin:swc-loader',
options: {
jsc: {
parser: {
syntax: 'typescript',
tsx: true,
},
transform: {
react: {
runtime: 'automatic',
},
},
},
},
},
type: 'javascript/auto',
},
],
},
};
CSSローダー
module.exports = {
module: {
rules: [
// 通常のCSS
{
test: /\.css$/,
use: ['css-loader', 'postcss-loader'],
type: 'css',
},
// Sass/SCSS
{
test: /\.scss$/,
use: ['css-loader', 'sass-loader'],
type: 'css',
},
// Less
{
test: /\.less$/,
use: ['css-loader', 'less-loader'],
type: 'css',
},
],
},
};
アセットローダー
module.exports = {
module: {
rules: [
// 画像ファイル
{
test: /\.(png|jpg|jpeg|gif|svg)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024, // 8KB以下はinline
},
},
},
// フォントファイル
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
type: 'asset/resource',
},
],
},
};
コード分割とチャンク最適化
エントリーポイント分割
module.exports = {
entry: {
main: './src/index.js',
admin: './src/admin.js',
vendor: ['react', 'react-dom'],
},
output: {
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].chunk.js',
},
};
動的インポート
// src/index.js
// 動的インポートで自動コード分割
const loadHeavyModule = async () => {
const module = await import('./heavyModule');
module.doSomething();
};
// Reactでの使用例
const LazyComponent = React.lazy(() => import('./LazyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
);
}
最適化設定
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
// ベンダーコード分離
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10,
},
// 共通コード分離
common: {
minChunks: 2,
priority: 5,
reuseExistingChunk: true,
name: 'common',
},
// React系ライブラリ分離
react: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: 'react',
priority: 20,
},
},
},
runtimeChunk: {
name: 'runtime',
},
minimize: true,
usedExports: true, // Tree shaking有効化
},
};
環境変数と定数定義
DefinePluginの使用
const rspack = require('@rspack/core');
module.exports = {
plugins: [
new rspack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
'process.env.API_URL': JSON.stringify(process.env.API_URL),
__DEV__: process.env.NODE_ENV !== 'production',
__VERSION__: JSON.stringify(require('./package.json').version),
}),
],
};
.envファイルの使用
# .env
API_URL=https://api.example.com
FEATURE_FLAG=true
// rspack.config.js
require('dotenv').config();
module.exports = {
plugins: [
new rspack.DefinePlugin({
'process.env.API_URL': JSON.stringify(process.env.API_URL),
'process.env.FEATURE_FLAG': JSON.stringify(process.env.FEATURE_FLAG),
}),
],
};
使用例:
// src/config.ts
export const config = {
apiUrl: process.env.API_URL,
isDev: __DEV__,
version: __VERSION__,
};
パフォーマンス最適化
キャッシュ戦略
module.exports = {
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename],
},
},
output: {
filename: '[name].[contenthash:8].js',
chunkFilename: '[name].[contenthash:8].chunk.js',
},
};
並列処理の最適化
module.exports = {
experiments: {
incrementalRebuild: true, // インクリメンタルビルド
},
optimization: {
moduleIds: 'deterministic',
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
maxInitialRequests: 25,
minSize: 20000,
},
},
};
Tree Shakingの最適化
module.exports = {
optimization: {
usedExports: true,
sideEffects: true,
},
};
// package.json
{
"sideEffects": false
}
または特定ファイルのみ副作用あり:
{
"sideEffects": ["*.css", "*.scss", "./src/polyfills.js"]
}
モダンブラウザ向けビルド
ターゲット設定
module.exports = {
target: ['web', 'es2020'],
module: {
rules: [
{
test: /\.(js|jsx|ts|tsx)$/,
use: {
loader: 'builtin:swc-loader',
options: {
jsc: {
target: 'es2020',
parser: {
syntax: 'typescript',
tsx: true,
},
},
env: {
targets: {
chrome: '90',
firefox: '88',
safari: '14',
edge: '90',
},
},
},
},
},
],
},
};
モダンとレガシーの二重ビルド
// rspack.modern.config.js
module.exports = {
output: {
filename: '[name].modern.js',
},
target: ['web', 'es2020'],
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: {
loader: 'builtin:swc-loader',
options: {
jsc: {
target: 'es2020',
},
},
},
},
],
},
};
// rspack.legacy.config.js
module.exports = {
output: {
filename: '[name].legacy.js',
},
target: ['web', 'es5'],
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: {
loader: 'builtin:swc-loader',
options: {
jsc: {
target: 'es5',
},
},
},
},
],
},
};
HTMLで条件付き読み込み:
<script type="module" src="/dist/main.modern.js"></script>
<script nomodule src="/dist/main.legacy.js"></script>
Dev Serverの設定
module.exports = {
devServer: {
port: 3000,
hot: true,
open: true,
historyApiFallback: true,
compress: true,
proxy: {
'/api': {
target: 'http://localhost:4000',
changeOrigin: true,
pathRewrite: { '^/api': '' },
},
},
headers: {
'Access-Control-Allow-Origin': '*',
},
client: {
overlay: {
errors: true,
warnings: false,
},
},
},
};
プロダクションビルドの最適化
完全な最適化設定
const rspack = require('@rspack/core');
const CompressionPlugin = require('compression-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
module.exports = {
mode: 'production',
devtool: 'source-map',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].[contenthash:8].js',
chunkFilename: 'js/[name].[contenthash:8].chunk.js',
assetModuleFilename: 'assets/[name].[hash:8][ext]',
clean: true,
},
optimization: {
minimize: true,
minimizer: [
new rspack.SwcJsMinimizerRspackPlugin(),
new rspack.LightningCssMinimizerRspackPlugin(),
],
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10,
},
},
},
runtimeChunk: 'single',
moduleIds: 'deterministic',
chunkIds: 'deterministic',
},
performance: {
maxEntrypointSize: 512000,
maxAssetSize: 512000,
hints: 'warning',
},
plugins: [
new CompressionPlugin({
algorithm: 'gzip',
test: /\.(js|css|html|svg)$/,
threshold: 10240,
minRatio: 0.8,
}),
new BundleAnalyzerPlugin({
analyzerMode: 'static',
openAnalyzer: false,
}),
],
};
モノレポ対応
// packages/app/rspack.config.js
const path = require('path');
module.exports = {
resolve: {
alias: {
'@shared': path.resolve(__dirname, '../shared/src'),
'@utils': path.resolve(__dirname, '../utils/src'),
},
extensions: ['.ts', '.tsx', '.js', '.jsx'],
},
module: {
rules: [
{
test: /\.(ts|tsx)$/,
include: [
path.resolve(__dirname, 'src'),
path.resolve(__dirname, '../shared/src'),
path.resolve(__dirname, '../utils/src'),
],
use: {
loader: 'builtin:swc-loader',
},
},
],
},
};
デバッグとトラブルシューティング
ビルド情報の詳細表示
module.exports = {
stats: {
preset: 'verbose',
colors: true,
modules: true,
chunks: true,
chunkModules: true,
reasons: true,
timings: true,
},
};
ソースマップの設定
module.exports = {
// 開発環境
devtool: 'eval-cheap-module-source-map',
// 本番環境
// devtool: 'source-map',
// デバッグなし
// devtool: false,
};
ビルドパフォーマンスの測定
# ビルド時間の測定
time npm run build
# 詳細なプロファイリング
RSPACK_PROFILE=ALL npm run build
実践的な設定例
大規模SPAの設定
const rspack = require('@rspack/core');
const path = require('path');
module.exports = {
entry: './src/index.tsx',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].[contenthash:8].js',
chunkFilename: 'js/[name].[contenthash:8].chunk.js',
publicPath: '/',
clean: true,
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
alias: {
'@': path.resolve(__dirname, 'src'),
'@components': path.resolve(__dirname, 'src/components'),
'@utils': path.resolve(__dirname, 'src/utils'),
'@hooks': path.resolve(__dirname, 'src/hooks'),
},
},
module: {
rules: [
{
test: /\.(ts|tsx)$/,
use: {
loader: 'builtin:swc-loader',
options: {
jsc: {
parser: {
syntax: 'typescript',
tsx: true,
},
transform: {
react: {
runtime: 'automatic',
},
},
},
},
},
},
{
test: /\.css$/,
use: ['css-loader', 'postcss-loader'],
type: 'css',
},
{
test: /\.(png|jpg|jpeg|gif|svg|webp)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 10 * 1024,
},
},
},
],
},
plugins: [
new rspack.HtmlRspackPlugin({
template: './public/index.html',
minify: true,
}),
new rspack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
}),
],
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10,
},
react: {
test: /[\\/]node_modules[\\/](react|react-dom|react-router-dom)[\\/]/,
name: 'react',
priority: 20,
},
},
},
runtimeChunk: 'single',
},
devServer: {
port: 3000,
hot: true,
historyApiFallback: true,
},
};
まとめ
Rspackは、Webpackの互換性と次世代のパフォーマンスを両立した理想的なバンドラーです。
主な利点:
- Rust実装による圧倒的な高速化(10〜30倍)
- Webpack互換で段階的な移行が可能
- ビルトインSWCローダーで追加設定不要
- 高速HMRで開発体験が向上
- 大規模プロジェクトでも短時間でビルド完了
こんなプロジェクトに最適:
- 大規模SPAでビルド時間に課題がある
- Webpackから移行したいがエコシステムを維持したい
- 開発体験を向上させたい
- モノレポ構成で複数パッケージをビルド
Rspackは、Webpackの設計思想を受け継ぎながら、現代のパフォーマンス要求に応える次世代バンドラーです。既存のWebpackプロジェクトから段階的に移行でき、即座にビルド時間の大幅短縮を実感できます。