今回は、Webpackを使ってSassをコンパイルする方法をはじめ、コンパイルされたCSSにベンダープレフィックスを付与する方法や、JavaScriptファイルにバンドルする方法、さらにCSSを別ファイルとして出力・管理する方法について解説します。
本記事は、Webpack解説シリーズの「【Webpack入門】インストール方法とJavaScriptファイルのバンドル手順」の続きになります。
WebpackでSass・CSSを扱う方法
SassファイルをCSSにコンパイルし、JavaScriptにバンドルする方法
WebpackでJavaScript以外のファイルを扱うためには、ローダー(Loader)という仕組みを使って変換・読み込みを行います。
Webpackで、JavaScript以外のファイルをJavaScriptとして扱える形に変換する仕組み。
module.rules配列の中に記述し、rules配列の中では、オブジェクトごとに「どのファイルに、どのローダーを適用するか」を定義していく。
まずは、以下のように4つのモジュールをインストールします。
npm install sass sass-loader css-loader style-loader --save-dev| モジュール名 | 役割 |
|---|---|
| sass | .scss / .sassをCSSに変換するコンパイラ |
| sass-loader | Webpackからsassを呼び出してSass→CSSを実行するローダー |
| css-loader | CSSをJavaScriptからimportできる形に変換するローダー |
| style-loader | 変換されたCSSを<style>タグとしてブラウザに挿入するローダー |
続いて、インストールしたローダーをwebpack.config.js上に設定します。
ローダーの設定はmodule.rules配列の中に記述し、rules配列の中では、オブジェクトごとに「どのファイルに、どのローダーを適用するか」を定義します。
const path = require("node:path");
module.exports = {
mode: "production",
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].js",
},
module: {
rules: [
{
test: /\.scss$/, //どの拡張子のファイルに適用させるかを指定
use: [
//変換されたCSSを<style>タグとしてブラウザに挿入
"style-loader",
//CSSをJavaScriptからimportできる形に変換
"css-loader",
//Webpackからsassを呼び出してSass→CSSを実行
"sass-loader",
],
},
],
},
};testで、どの拡張子のファイルに適用するかを正規表現で指定し、useの中に配列形式で適用したいローダーを指定します。
この際、ローダーは配列の右から左の順番で適用されるので、sass-loader→css-loader→style-loaderとなる順番に指定します。
これで、実際にSassファイルがきちんとCSSにコンパイルされて、JavaScriptファイルにバンドルされるか確認したいので、src/style.scssファイルを作成し、エントリーポイントとなるindex.jsからimportします。
h1 {
font-size: 30px;
&:hover {
color: red;
}
}import { sayHello } from "./sub";
import "./style.scss";
sayHello("Webpack");これで、webpackコマンドを実行すると、Sassファイルがmain.jsにバンドルされます。
このmain.jsを、dist/index.htmlを作成して、そこから読み込み、実際にstyleタグが注入されているか確認してみます。
<!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>
<h1>ここにテキストが入ります。</h1>
<script src="./main.js"></script>
</body>
</html>
なお、SassからコンパイルされたCSSをJavaScriptにバンドルせず、別のCSSファイルとして出力し、それをHTMLから読み込むことも可能です。
そのやり方については、「CSSを別ファイルとして分離する方法」で解説します。
PostCSSを使ってベンダープレフィックスを自動付与する方法
続いて、PostCSSを使ってベンダープレフィックスを自動付与する方法を解説します。
PostCSSとは、CSSファイルに対して、特定の処理を加えるためのツールです。
PostCSSでは、プラグインを組み合わせて使うことで、さまざまな処理をCSSに対して行うことができます。
今回はその中でも、ベンダープレフィックスを自動で付与してくれるプラグイン「Autoprefixer」を使用します。
まずは、必要なモジュールをインストールします。
npm install -D postcss postcss-loader autoprefixer| モジュール名 | 役割 |
|---|---|
| postcss | CSSを解析・変換するPostCSS本体 |
| postcss-loader | WebpackからPostCSSを呼び出すローダー |
| autoprefixer | ベンダープレフィックスを付与するPostCSSプラグイン |
続いて、Webpackの設定ファイル内にpostcss-loaderを追加します。
追加位置は、css-loaderの直前に配置します。
const path = require("node:path");
module.exports = {
mode: "production",
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].js",
},
module: {
rules: [
{
test: /\.scss$/, //どの拡張子のファイルに適用させるかを指定
use: [
//変換されたCSSを<style>タグとしてブラウザに挿入
"style-loader",
//CSSをJavaScriptからimportできる形に変換
"css-loader",
//WebpackからPostCSSを呼び出して実行
"postcss-loader",
//Webpackからsassを呼び出してSass→CSSを実行
"sass-loader",
],
},
],
},
};最後に、PostCSSの設定ファイルをpostcss.config.jsファイルを作成し、以下のように記述します。
module.exports = {
plugins: [
require('autoprefixer')
]
};この設定を行ったうえでwebpackコマンドを実行すると、CSSのビルド時にpostcss-loaderが読み込まれ、autoprefixerによって必要なベンダープレフィックスが自動的に付与されます。
なお、ベンダープレフィックスを付与するかどうかの判断は、package.jsonのbrowserslist、または.browserslistrcファイルに記述されたBrowserslistの設定内容に基づいて行われます。
Browserslistを明示的に設定していない場合は、次のdefaults設定が自動的に適用されます。
> 0.5%, last 2 versions, Firefox ESR, not dead- > 0.5% ・・・ 世界シェアが0.5%以上のブラウザを対象
- last 2 versions ・・・ 各ブラウザの最新2バージョン
- Firefox ESR ・・・ Firefox の長期サポート版(ESR)を含める
- not dead ・・・ すでにサポート終了しているブラウザを除外
CSSを別ファイルとして分離する方法
sass-loader→postcss-loader→css-loader→style-loaderという順番でSassファイルを処理している場合、最終的なCSSはJavaScriptファイルにバンドルされ、JavaScriptの実行時に<style>タグとしてDOMに注入されます。
これを、JavaScriptとは別のCSSファイルとして出力したい場合には、WebpackのプラグインであるMiniCssExtractPluginを使用します。
まずは、プラグインをインストールします。
npm install --save-dev mini-css-extract-plugin続いて、webpack.config.jsファイル内でプラグインを読み込み、pluginsに追加したうえで、CSSを処理するローダーとしてstyle-loaderの代わりにMiniCssExtractPlugin.loaderを設定します。
const path = require("node:path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
mode: "production",
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].js",
},
plugins: [new MiniCssExtractPlugin()],
module: {
rules: [
{
test: /\.scss$/, //どの拡張子のファイルに適用させるかを指定
use: [
//変換されたCSSを<style>タグとしてブラウザに挿入
// "style-loader", 削除
//変換されたCSSをJavaScriptとは別のCSSファイルとして抽出
MiniCssExtractPlugin.loader,
//CSSをJavaScriptからimportできる形に変換
"css-loader",
//WebpackからPostCSSを呼び出して実行
"postcss-loader",
//Webpackからsassを呼び出してSass→CSSを実行
"sass-loader",
],
},
],
},
};これで、webpackコマンドを実行すると、これまでmain.jsにバンドルされていたCSSが分割されて、dist/main.cssが作成されます。
なお、MiniCssExtractPluginで出力されるCSSファイル名は、デフォルトでWebpackのエントリー名が使用されます。(エントリーポイントがデフォルト設定のままで1つのみの場合は、main.cssとなります)

このファイル名を任意の名前に変更したい場合は、プラグインのオプションでfilenameを指定します。
const path = require("node:path"); // Node.jsに標準で用意されているpathモジュールの読み込み
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
mode: "production",
entry: "./src/index.js", // エントリーポイントの指定
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].js", // 出力時のファイル名の指定
},
plugins: [
new MiniCssExtractPlugin({
filename: "style.css",
}),
],
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",
],
},
],
},
};これでstyle.cssという名前でCSSファイルが生成されます。

なお、1点注意点として、複数のエントリーがあり、それぞれからSassファイルを読み込んでいる場合、全ての複数のエントリーポイントから抽出されたCSSが、すべて同じファイル名(style.css)で出力しようとするため、エラーが起きてしまいます。
const path = require("node:path"); // Node.jsに標準で用意されているpathモジュールの読み込み
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
mode: "production",
entry: { main: "./src/index.js", sub: "./src/sub.js" }, // エントリーポイントの指定
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].js", // 出力時のファイル名の指定
},
plugins: [
new MiniCssExtractPlugin({
filename: "style.css",
}),
],
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",
],
},
],
},
};import "./style.scss";
console.log("index.js")import "./sub.scss";
console.log("sub.js");
そのため、出力ファイル名を固定したい場合は、特定のエントリーポイントでのみSassを読み込むように制限するか、「index.jsで読み込んだCSSはstyle.cssとして出力し、それ以外はエントリー名に基づいた名前にする」といった出し分けの設定が必要です。
MiniCssExtractPluginのfilenameに関数を指定すると、引数のpathDataから取得できるエントリー情報を活用し、ファイル名や出力先を動的に制御できるようになります。
const path = require("node:path"); // Node.jsに標準で用意されているpathモジュールの読み込み
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
mode: "production",
entry: { main: "./src/index.js", sub: "./src/sub.js" }, // エントリーポイントの指定
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].js", // 出力時のファイル名の指定
},
plugins: [
new MiniCssExtractPlugin({
filename: (pathData) => {
// エントリー名が'main'の場合は'style.css'を返す
// それ以外はエントリー名そのまま([name].css)にする
return pathData.chunk.name === "main" ? "style.css" : "[name].css";
},
}),
],
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",
],
},
],
},
};
まとめ
今回は、WebpackでSassをコンパイルしてCSSを扱う方法を解説しました。
Webpackを活用すれば他にも、特定のルールに基づいたCSSの構文チェックや、プロパティのアルファベット順ソートなども自動化できます。
本記事では、その辺りに関しては解説しませんでしたが、気になる方はぜひ調べてみて下さい。
次回は、WebpackでコンパイルされたJavaScriptファイルやCSSファイルをHTMLファイルに自動注入する方法を解説します。
(関連記事)Webpackでバンドル後のJavaScriptをHTMLに自動で読み込ませる方法
今回は以上になります。最後までご覧頂き、ありがとうございました。
Webサイトのスポット修正・保守ならお任せ下さい!
ちょっとした修正や調整をしたいけれど、「制作会社に頼んだら高額になりそう」「どこに相談すればいいかわからない」そんなお悩みはありませんか?
弊所「E-VALUE WORKS」では、テキスト修正や軽微な調整などのスポット対応から、月額での保守・管理(外部Web担当)まで、ニーズに合わせたWebサイトの修正・保守サービスを提供しています。
「これって依頼できる?」という段階でも構いません。まずはお気軽にご相談ください。