お役立ちコラム
フロントエンド

WebpackでSassをコンパイルしてCSSを扱う方法

※本記事には広告が含まれます

今回は、Webpackを使ってSassをコンパイルする方法をはじめ、コンパイルされたCSSにベンダープレフィックスを付与する方法や、JavaScriptファイルにバンドルする方法、さらにCSSを別ファイルとして出力・管理する方法について解説します。

本記事は、Webpack解説シリーズの「【Webpack入門】インストール方法とJavaScriptファイルのバンドル手順」の続きになります。

WebpackでSass・CSSを扱う方法

SassファイルをCSSにコンパイルし、JavaScriptにバンドルする方法

WebpackでJavaScript以外のファイルを扱うためには、ローダー(Loader)という仕組みを使って変換・読み込みを行います。

ローダー(Loader)とは?

Webpackで、JavaScript以外のファイルをJavaScriptとして扱える形に変換する仕組み。

module.rules配列の中に記述し、rules配列の中では、オブジェクトごとに「どのファイルに、どのローダーを適用するか」を定義していく。

まずは、以下のように4つのモジュールをインストールします。

npm install sass sass-loader css-loader style-loader --save-dev
モジュール名役割
sass.scss / .sassをCSSに変換するコンパイラ
sass-loaderWebpackからsassを呼び出してSass→CSSを実行するローダー
css-loaderCSSを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
モジュール名役割
postcssCSSを解析・変換するPostCSS本体
postcss-loaderWebpackから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サイトの修正・保守サービスを提供しています。

「これって依頼できる?」という段階でも構いません。まずはお気軽にご相談ください。

E-VALUE WORKS アイコン
HOMEお役立ちコラムフロントエンドWebpackでSassをコンパイルしてCSSを扱う方法