webpack の使い方 — JavaScriptモジュールバンドラーの決定版
一言でいうと
webpackは、JavaScript/TypeScriptをはじめとするあらゆるアセットを依存関係を解決しながらバンドル(結合)するモジュールバンドラーです。ES Modules・CommonJS・AMDの各モジュール形式に対応し、コード分割やローダーによるファイル変換など、フロントエンド開発に必要なビルド機能を包括的に提供します。
どんな時に使う?
- フロントエンドアプリケーションのビルド — 複数のJavaScript/TypeScriptファイルをブラウザで実行可能な形にバンドルしたいとき
- コード分割(Code Splitting)による最適化 — 初期ロード時間を短縮するために、アプリケーションを複数のチャンクに分割し、必要なタイミングで非同期読み込みしたいとき
- CSS・画像・フォントなど非JSアセットの統合管理 — ローダーを使ってTypeScript→JavaScript変換、Sass→CSS変換、画像のBase64化などをビルドパイプラインに組み込みたいとき
インストール
# npm
npm install --save-dev webpack webpack-cli
# yarn
yarn add --dev webpack webpack-cli
# pnpm
pnpm add -D webpack webpack-cli
注意:
webpack本体に加えて、CLIから実行するためにwebpack-cliも必要です。
基本的な使い方
プロジェクト構成
my-app/
├── src/
│ └── index.ts
├── dist/
├── webpack.config.js
├── tsconfig.json
└── package.json
webpack.config.js(TypeScript対応の基本構成)
// webpack.config.js
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/index.ts',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
resolve: {
extensions: ['.ts', '.tsx', '.js'],
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
};
src/index.ts
import { greet } from './utils';
const message: string = greet('webpack');
console.log(message);
ビルド実行
npx webpack
package.json にスクリプトを追加しておくのが一般的です。
{
"scripts": {
"build": "webpack --mode production",
"dev": "webpack serve --mode development"
}
}
よく使うAPI・設定オプション — webpack の使い方を深掘り
1. entry — エントリーポイントの設定
バンドルの起点となるファイルを指定します。複数エントリーにも対応しています。
// 単一エントリー
module.exports = {
entry: './src/index.ts',
};
// 複数エントリー(マルチページアプリケーション向け)
module.exports = {
entry: {
main: './src/index.ts',
admin: './src/admin.ts',
},
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist'),
},
};
2. module.rules(ローダー) — ファイル変換の設定
webpack単体ではJavaScriptとJSONしか理解できません。ローダーを使うことで、あらゆるファイル形式をモジュールとして扱えます。
module.exports = {
module: {
rules: [
// TypeScript
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
// CSS + CSS Modules
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
// Sass/SCSS
{
test: /\.s[ac]ss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
// 画像ファイル(webpack 5 の Asset Modules)
{
test: /\.(png|jpg|gif|svg)$/,
type: 'asset/resource',
},
],
},
};
ポイント:
use配列のローダーは**右から左(下から上)**の順に適用されます。['style-loader', 'css-loader', 'sass-loader']の場合、Sass → CSS → DOMへの注入の順で処理されます。
3. plugins — ビルドプロセスの拡張
プラグインはバンドル全体に対する処理を行います。HTML生成、CSS抽出、環境変数注入など多岐にわたります。
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const webpack = require('webpack');
module.exports = {
plugins: [
// HTMLファイルを自動生成し、バンドルを自動挿入
new HtmlWebpackPlugin({
template: './src/index.html',
title: 'My App',
}),
// CSSを別ファイルとして抽出(本番向け)
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css',
}),
// 環境変数をコード内で参照可能にする
new webpack.DefinePlugin({
'process.env.API_URL': JSON.stringify('https://api.example.com'),
}),
],
};
4. optimization — コード分割と最適化
module.exports = {
optimization: {
// コード分割の設定
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
// ランタイムコードを別チャンクに分離
runtimeChunk: 'single',
// 本番ビルド時のminify(mode: 'production' ではデフォルトで有効)
minimize: true,
},
};
5. devServer — 開発サーバーの設定
webpack-dev-server パッケージを追加インストールすると、HMR(Hot Module Replacement)対応の開発サーバーが使えます。
npm install --save-dev webpack-dev-server
module.exports = {
devServer: {
static: './dist',
port: 3000,
hot: true,
open: true,
// APIプロキシ設定
proxy: [
{
context: ['/api'],
target: 'http://localhost:8080',
changeOrigin: true,
},
],
historyApiFallback: true, // SPA向け:全ルートをindex.htmlにフォールバック
},
};
npx webpack serve
実践的な設定例:開発/本番の切り替え
// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = (env, argv) => {
const isProduction = argv.mode === 'production';
return {
entry: './src/index.ts',
output: {
filename: isProduction ? '[name].[contenthash].js' : '[name].js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
devtool: isProduction ? 'source-map' : 'eval-source-map',
resolve: {
extensions: ['.ts', '.tsx', '.js'],
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{
test: /\.css$/,
use: [
isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
'css-loader',
],
},
{
test: /\.(png|jpg|gif|svg|woff2?)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024, // 8KB未満はインライン化
},
},
},
],
},
plugins: [
new HtmlWebpackPlugin({ template: './src/index.html' }),
...(isProduction
? [new MiniCssExtractPlugin({ filename: '[name].[contenthash].css' })]
: []),
],
optimization: {
splitChunks: { chunks: 'all' },
},
devServer: {
port: 3000,
hot: true,
},
};
};
類似パッケージとの比較
| 項目 | webpack | Vite | Rollup | esbuild |
|---|---|---|---|---|
| 主な用途 | アプリケーションバンドル | アプリケーション開発・バンドル | ライブラリバンドル | 超高速バンドル/トランスパイル |
| 開発サーバー速度 | △(バンドル後に配信) | ◎(ESM native、HMR高速) | ×(別途必要) | ○ |
| 設定の複雑さ | 高い | 低い | 中程度 | 低い |
| プラグインエコシステム | ◎(最大規模) | ○(Rollupプラグイン互換) | ○ | △ |
| Tree Shaking | ○ | ◎ | ◎ | ○ |
| コード分割 | ◎ | ◎ | ○ | △ |
| レガシーブラウザ対応 | ◎ | ○(プラグインで対応) | ○ | △ |
| 成熟度・実績 | ◎(10年以上の歴史) | ○(急速に普及中) | ◎ | ○ |
選定の目安:
- 新規プロジェクト → Viteが開発体験で優位。特にReact/Vue/Svelteなどのフレームワーク利用時
- 既存の大規模プロジェクト → webpackの豊富なプラグインとローダーが活きる
- ライブラリ開発 → Rollupが出力の最適化に強い
- ビルド速度最優先 → esbuildが圧倒的に高速
注意点・Tips
1. webpack 5 の Asset Modules を使う
webpack 5 では file-loader、url-loader、raw-loader が不要になりました。代わりに組み込みの Asset Modules を使いましょう。
// ❌ webpack 4 以前のやり方
{ test: /\.png$/, use: 'file-loader' }
// ✅ webpack 5
{ test: /\.png$/, type: 'asset/resource' }
| type | 旧ローダー相当 | 動作 |
|---|---|---|
asset/resource | file-loader | ファイルを出力し、URLを返す |
asset/inline | url-loader | Base64データURLとしてインライン化 |
asset/source | raw-loader | ソースコードをそのまま文字列として返す |
asset | url-loader(size制限付き) | サイズに応じて自動判定 |
2. contenthash でキャッシュバスティング
本番ビルドでは [contenthash] を使うことで、ファイル内容が変わったときだけファイル名が変わり、ブラウザキャッシュを効率的に活用できます。
output: {
filename: '[name].[contenthash:8].js', // 8文字に短縮も可能
}
3. resolve.alias でインポートパスを簡潔に
resolve: {
alias: {
'@components': path.resolve(__dirname, 'src/components'),
'@utils': path.resolve(__dirname, 'src/utils'),
},
}
// ❌ 相対パスが深くなりがち
import { Button } from '../../../components/Button';
// ✅ エイリアスですっきり
import { Button } from '@components/Button';
注意: TypeScriptを使う場合は
tsconfig.jsonのpathsにも同じエイリアスを設定する必要があります。
4. ビルドサイズの分析
バンドルサイズが肥大化したら webpack-bundle-analyzer で可視化しましょう。
npm install --save-dev webpack-bundle-analyzer
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
module.exports = {
plugins: [
new BundleAnalyzerPlugin({ analyzerMode: 'static' }),
],
};
5. Node.js API からの利用
CLIではなくプログラムからwebpackを実行することも可能です。
import webpack, { Configuration, Stats } from 'webpack';
const config: Configuration = {
mode: 'production',
entry: './src/index.ts',
output: {
filename: 'bundle.js',
path: __dirname + '/dist',
},
};
const compiler = webpack(config);
compiler.run((err: Error | null, stats?: Stats) => {
if (err) {
console.error(err);