Webpackは、エントリポイントを起点に依存関係をたどり、複数のJavaScriptファイルを1つにバンドルします。
その過程で、JSやCSSから画像ファイルへの参照を検知すると、それらもプロジェクトのアセットとしてバンドル対象に含めます。
今回は、Webpackの「Asset Modules」機能を用いて、これらの画像を処理する方法を解説します。
※本記事は、Webpack 5(v5.104.1)を使用した環境を前提としています。
Webpackで画像をバンドルする方法
エントリーポイントでインポートされているSassファイル内で背景画像等の指定がある場合、その画像ファイルはWebpackによって依存関係として認識され、バンドルの対象になります。
.pageheader {
background-image: url("./img/pageheader_bg.jpg");
background-size: cover;
}
h1 {
font-size: 30px;
display: flex;
&:hover {
color: red;
}
}import { sayHello } from "./sub";
import "./style.scss";
sayHello("Hi, Webpack");そのため、画像ファイルに対してもWebpack側で適切な設定を行う必要があります。
Webpack 4では、画像を読み込むために新たにローダーを追加する必要がありましたが、Webpack 5以降では、画像やフォントなどの静的ファイルを扱うために「Asset Modules」という仕組みが用意されています。
Asset Modules は、他のローダーと同様にmodule.rules配列の中に記述して設定します。
const path = require("node:path"); // Node.jsに標準で用意されているpathモジュールの読み込み
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "production",
entry: "./src/index.js", // エントリーポイントの指定
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].[contenthash].js", // 出力時のファイル名の指定
},
plugins: [
new MiniCssExtractPlugin({
filename: (pathData) => {
// エントリー名が'main'の場合は'style.css'を返す
// それ以外はエントリー名そのまま([name].css)にする
return pathData.chunk.name === "main"
? "style.[contenthash].css"
: "[name].[contenthash].css";
},
}),
new HtmlWebpackPlugin({
// ビルド時に使用するHTMLテンプレートを指定
template: "./src/index.html",
// 生成されたスクリプトタグを挿入する位置(bodyの末尾)
inject: "body",
// このHTMLに自動で読み込ませるエントリーポイントを指定(複数指定する場合は配列の中でカンマ区切りで指定)
chunks: ["main"],
}),
],
module: {
rules: [
{
test: /\.scss$/, //どの拡張子のファイルに適用させるかを指定
use: [
//変換されたCSSをJavaScriptとは別のCSSファイルとして抽出
MiniCssExtractPlugin.loader,
//CSSをJavaScriptからimportできる形に変換
"css-loader",
//WebpackからPostCSSを呼び出して実行
"postcss-loader",
//Webpackからsassを呼び出してSass→CSSを実行
"sass-loader",
],
},
{
test: /\.(png|jpe?g|gif|svg)$/i,
//画像を別ファイルとして出力
type: "asset/resource",
},
],
},
};上の例では、typeを「asset/resource」と設定していますが、この設定を行うことで、画像ファイルはJavaScriptにバンドルされるのではなく、ビルドディレクトリ直下に複製・出力されます。(Webpack 4まで使用されていたfile-loaderと同等の挙動。)
| type値 | 意味 |
|---|---|
| asset/resource | 画像を別ファイルとして出力し、そのURLをJavaScriptやCSS側に渡す。 |
| asset/inline | 画像をBase64(data URI)としてJavaScriptやCSSに埋め込む。 |
| asset | ファイルサイズに応じて、Base64(data URI)として埋め込むか、別ファイルとして出力するかを自動的に切り替える。 |
また、デフォルトではファイル名がハッシュ値を使った名前(以下では6bc7b07e373c60e65f7e.jpg)で出力されます。


ハッシュとは、特定の計算手順を用いて、元のデータを一見ランダムに見える値に変換することを言います。
このとき、変換に用いる計算手順を「ハッシュ関数」、それによって算出された値を「ハッシュ値」と呼びます。
これをビルドディレクトリ直下ではなく、dist/imgフォルダにまとめたい場合は、新たにgenerator.filenameを指定します。
const path = require("node:path"); // Node.jsに標準で用意されているpathモジュールの読み込み
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "production",
entry: "./src/index.js", // エントリーポイントの指定
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].[contenthash].js", // 出力時のファイル名の指定
},
plugins: [
//省略
],
module: {
rules: [
{
test: /\.scss$/, //どの拡張子のファイルに適用させるかを指定
use: [
//変換されたCSSをJavaScriptとは別のCSSファイルとして抽出
MiniCssExtractPlugin.loader,
//CSSをJavaScriptからimportできる形に変換
"css-loader",
//WebpackからPostCSSを呼び出して実行
"postcss-loader",
//Webpackからsassを呼び出してSass→CSSを実行
"sass-loader",
],
},
{
test: /\.(png|jpe?g|gif|svg)$/i,
//画像を別ファイルとして出力
type: "asset/resource",
generator: {
//出力先とファイル名を指定
filename: "img/[name].[contenthash][ext]",
},
},
],
},
};※[ext]には、先頭にドット(.)が含まれているため、[name].[contenthash].[ext]にしてしまわないように注意。
これで、画像ファイルは「元のファイル名.ハッシュ値.拡張子」の名前でdist配下のimgフォルダにまとめて出力されます。

HTMLの<img>タグに記述したパスもWebpackに検知させる方法
HTMLの<img>タグに記述したパスもWebpackに検知させて、Asset Modulesの対象にしたい場合は、html-loaderをインストールします。
npm install --save-dev html-loaderインストールができたら、module.rules配列の新たに設定を追加します。
const path = require("node:path"); // Node.jsに標準で用意されているpathモジュールの読み込み
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "production",
entry: "./src/index.js", // エントリーポイントの指定
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].[contenthash].js", // 出力時のファイル名の指定
},
plugins: [
new MiniCssExtractPlugin({
filename: (pathData) => {
// エントリー名が'main'の場合は'style.css'を返す
// それ以外はエントリー名そのまま([name].css)にする
return pathData.chunk.name === "main"
? "style.[contenthash].css"
: "[name].[contenthash].css";
},
}),
new HtmlWebpackPlugin({
// ビルド時に使用するHTMLテンプレートを指定
template: "./src/index.html",
// 生成されたスクリプトタグを挿入する位置(bodyの末尾)
inject: "body",
// このHTMLに自動で読み込ませるエントリーポイントを指定(複数指定する場合は配列の中でカンマ区切りで指定)
chunks: ["main"],
}),
],
module: {
rules: [
{
test: /\.scss$/, //どの拡張子のファイルに適用させるかを指定
use: [
//変換されたCSSをJavaScriptとは別のCSSファイルとして抽出
MiniCssExtractPlugin.loader,
//CSSをJavaScriptからimportできる形に変換
"css-loader",
//WebpackからPostCSSを呼び出して実行
"postcss-loader",
//Webpackからsassを呼び出してSass→CSSを実行
"sass-loader",
],
},
{
test: /\.(png|jpe?g|gif|svg)$/i,
//画像を別ファイルとして出力
type: "asset/resource",
generator: {
//出力先とファイル名を指定
filename: "img/[name].[contenthash][ext]",
},
},
{
test: /\.html$/,
//HTMLをJavaScriptからimportできる形に変換
use: ["html-loader"],
},
],
},
};
これで、HTMLファイルをJavaScriptファイルからimportできるようになり、エントリーポイントからimportされているHTMLファイルについては、html-loaderを通してWebpackが内容を解析・処理できるようになります。
ただし、HtmlWebpackPluginを使用している場合は、JavaScriptから明示的にimportしていなくても、プラグイン側でtemplateに指定したHTMLファイルについては、html-loaderを通してWebpackが処理することになります。
これでnpx webpackコマンドを実行すると、HTML内で使用している画像についてもAsset Modulesの設定が適用され、dist/imgディレクトリ内に出力されます。
また、出力先のHTML側では、画像の参照パスが自動的に書き換えられます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Webpackの使い方</title>
</head>
<body>
<div class="pageheader">
<h1>ここにテキストが入ります。</h1>
</div>
<img src="./img/logo.svg" alt="ロゴ">
</body>
</html>
まとめ
今回は、Webpackの「Asset Modules」機能を使って画像を処理する方法を解説しました。
次回は、開発用と本番用でWebpackの設定ファイル(webpack.config.js)を分割する方法を解説します。
今回は以上になります。最後までご覧頂き、ありがとうございました。
Webサイトのスポット修正・保守ならお任せ下さい!
ちょっとした修正や調整をしたいけれど、「制作会社に頼んだら高額になりそう」「どこに相談すればいいかわからない」そんなお悩みはありませんか?
弊所「E-VALUE WORKS」では、テキスト修正や軽微な調整などのスポット対応から、月額での保守・管理(外部Web担当)まで、ニーズに合わせたWebサイトの修正・保守サービスを提供しています。
「これって依頼できる?」という段階でも構いません。まずはお気軽にご相談ください。