【コーダー向け】webpackでSassをコンパイルする方法

webpackでSassをコンパイルする方法です。webpackはHTMLコーダーにあまり優しくない印象(偏見)がありましたが、結論から言うとやりたいことは十分にやらせてくれます

具体的に実際にやっているのは、以下の5つです。

  • CSSファイルの出力
  • 構文チェック & 自動修正
  • ベンダープレフィックスの自動付与
  • プロパティのソート
  • メディアクエリの統合

webpackでSassをコンパイルしたい方はぜひ参考にしていただければと思います。

はにわまん
webpack触っていると、なんとなくフロントエンドエンジニア感がある

webpackの導入手順

my-webpack」というプロジェクトフォルダを作成します。作成場所はどこでもOKです。

コピーmkdir my-webpack

my-webpack」に移動。

コピーcd my-webpack

npmの初期化を行います。

コピーnpm init -y

最初の構成ファイルとしては、以下のように配置していますので、同様に進めたい方は作成しておいてください。

  • dist・・・ビルド後の出力先フォルダ
  • src・・・ビルド前の出力元フォルダ
  • index.html・・・静的HTMLファイル
  • webpack.config.js・・・webpackの設定ファイル

index.htmlは以下のように配置しています。

index.html
コピー<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>webpack Test</title> <link rel="stylesheet" href="dist/css/style.css"> </head> <body> <div id="main"> <p>テスト</p> </div> <script src="dist/my-bundle.js"></script> </body> </html>

CSSを読み込む基本の考え方

まずはwebpackにおけるCSSの読み込み方についておさらいしておきます。webpackの基本的な用途としては、JavaScriptやJSONを依存関係を考慮していい感じにまとめるといったビルドツールです。要するにJavaScript用ということです。

そんなwebpackですが、JavaScript開発と付随して使われがちなCSSやHTMLに関する拡張要素も豊富にあり、ローダーやプラグインで拡張していくことによって、フロントエンドの開発が効率的に行えるようになっていたりします。

ポイントとしては、

  • Sassをコンパイルするには拡張して足していくこと
  • webpackのローダー、プラグインという機能を活用すること

の2点を抑えておいてもらえたらと思います。
※ ローダーとプラグインという聞き慣れない単語かもしれませんが、両方ともwebpackの「拡張機能」という認識をしておけばなんとなくの理解はできるかと。

Sassをインラインで読み込む方法

それでは早速ローダーを使ってSassファイルをインラインで読み込んでいきましょう。インラインとは<head>内に直接記載されるCSSのことですね。

必要なローダーとしては以下のとおりです。インラインとして記載するために必要なのはstyle-loaderになります。なのでインライン記述にしない場合はこちらは不要ですが、css-loadersass-loaderはSassを使うならお決まりレベルで活用するローダーになります。

ローダーのインストール

  • style-loader・・・<head>内にインラインCSSで出力するためのローダー
  • css-loader・・・CSS記述を解釈するためのローダー
  • sass-loader・・・Sass記述を解釈するためのローダー

style-loaderのインストール。

コピーnpm install --save-dev style-loader

css-loaderのインストール。

コピーnpm install --save-dev css-loader

sass-loadersassのインストール。

コピーnpm install --save-dev sass-loader sass

webpackの設定

ローダーの指定は、webpack.config.jsmoduleに書いていきます。

webpack.config.js
コピーconst PATH = require('path'); module.exports = { mode: 'production', entry: './src/my-main.js', output: { path: PATH.resolve(__dirname, 'dist'), filename: 'my-bundle.js' }, module: { rules: [ { test: /\.scss$/i, use: [ { loader: 'style-loader', }, { loader: 'css-loader', }, { loader: 'sass-loader', options: { sassOptions: { outputStyle: 'expanded', }, }, }, ] }, ] } };

testに対象ファイルを指定して、useにローダーを指定します。

ローダーは定義した下から順番に読み込んでいくイメージです。この設定の場合は、sass-loaderが読み込まれ、次にcss-loader、最後にstyle-loaderという流れです。

Sassを解析して、CSSを解析して、インラインへ反映、みたいなイメージです。

その他の設定については、以下のwebpackの最小構成の記事を参考にしてください。

CSSのimport

エントリーポイントとなる、src/my-main.jsを作成し、style.scssを読み込ませます。

src/my-main.js
コピーimport './css/style.scss';

そして、同じ階層にcss/style.scssを作成して以下のように記述してあげます。

css/style.scss
コピーbody { background: #eee; } #main { p { color: #666; } }

これで準備はOKです。

webpackでビルド

ローダーの導入、ローダーの設定と必要ファイルの準備ができました。それではwebpackでビルドしてみましょう。

コピーnpx webpack

index.htmlを開いてみると、head内にコンパイルされたCSSが挿入されていることが分かるかと思います。

CSSを別ファイルに出力

インラインの流れでおおよそのSassからCSSにする方法が分かったかと思います。これでもサイトとしては表現可能ですが、CSSは別ファイルにした方が管理しやすいです。

次は、CSSを別ファイルとして出力する方法について見ていきましょう!

「MiniCssExtractPlugin」プラグインの導入

CSSを別ファイルに出力するためには、「MiniCssExtractPlugin」プラグインによって拡張する必要があります。
(公式) → MiniCssExtractPlugin | webpackMiniCssExtractPlugin | webpack

それでは早速インストールしていきましょう。

コピーnpm install --save-dev mini-css-extract-plugin

webpackの設定

webpack.config.jsへの設定は、インラインの指定から主にはstyle-loaderの部分を「MiniCssExtractPlugin」に変えるようなイメージです。

以下の追加を行います。

  • requireでMiniCssExtractPluginの読み込み
  • pluginsででMiniCssExtractPluginを追加
  • MiniCssExtractPluginをローダーに追加

全体のコードがこちらです。

webpack.config.js
コピーconst PATH = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { mode: 'production', entry: './src/my-main.js', output: { path: PATH.resolve(__dirname, 'dist'), filename: 'my-bundle.js' }, module: { rules: [ { test: /\.scss$/i, use: [ { loader: MiniCssExtractPlugin.loader, }, { loader: 'css-loader', }, { loader: 'sass-loader', options: { sassOptions: { outputStyle: 'expanded', }, }, }, ] }, ] }, plugins: [ new MiniCssExtractPlugin({ filename: 'css/style.css', ignoreOrder: true, }) ] };

意識的に指定するのは、pluginsで指定しているfilenameですね。ここに出力先のファイル名(+パス)を記載することができます。

このケースだと、cssフォルダ内のstyle.cssになります。ちなみに、この親フォルダは、outputで指定しているpathのフォルダです。

HTMLからCSSを読み込む

インラインではなく外部ファイルとなるので、HTMLからstyle.cssを読み込む必要があります。head内にdist/css/style.cssを読み込む記述を追加してあげましょう。

コピー<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>webpack Test</title> <link rel="stylesheet" href="dist/css/style.css"> </head>

webpackでビルド

それでは、webpackでビルドして出力されるか見てみましょう!

コピーnpx webpack

distフォルダの中にcss/style.cssが出来上がっていれば成功です。

CSSの構文チェックして自動修正する

CSSの構文チェックツールとしてstylelintがあります。webpackでstylelintを使って構文チェックからの自動修正まで行ってもらおうと思います。

stylelintの拡張プラグイン「StylelintWebpackPlugin」の導入

StylelintWebpackPluginによってwebpackにstylelintを導入することができます。
(公式) → https://webpack.js.org/plugins/stylelint-webpack-plugin/StylelintWebpackPlugin | webpack

webpackにStylelintWebpackPluginをインストールします。

コピーnpm install --save-dev stylelint-webpack-plugin

stylelint自体も必要なのでインストールしてください。

コピーnpm install --save-dev stylelint

webpackの設定

追加することは2つだけです。

  • 「StylelintWebpackPlugin」プラグインをrequire
  • pluginsにStylelintPluginを読み込み

コードの全体はこちら。

webpack.config.js
コピーconst PATH = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const StylelintPlugin = require('stylelint-webpack-plugin'); module.exports = { mode: 'production', entry: './src/my-main.js', output: { path: PATH.resolve(__dirname, 'dist'), filename: 'my-bundle.js' }, module: { rules: [ { test: /\.scss$/i, use: [ { loader: MiniCssExtractPlugin.loader, }, { loader: 'css-loader', }, { loader: 'sass-loader', options: { sassOptions: { outputStyle: 'expanded', }, }, }, ] }, ] }, plugins: [ new MiniCssExtractPlugin({ filename: 'css/style.css', ignoreOrder: true, }), new StylelintPlugin(), ] };

stylelintの定義ファイルを作成

.stylelintrcを作成して、以下のようにルールを定義してみました。

.stylelintrc
コピー{ "rules": { "no-extra-semicolons": true, "color-hex-length": "short", } }

「セミコロンは1個だけにしましょう」と、「16進数の値はできるだけ短く」という指定です。そしてあえてミスとなるようにstyle.scssを書き換えました。

src/css/style.scss
コピーbody { background: #eee;; } #main { p { color: #666666; } }

ビルドして実行結果を確認する

それでは、ビルドして結果を確認してみましょう!

コピーnpx webpack

しっかりエラーとして出力され、stylelintが機能していることが分かります。

stylelintに自動修正してもらう

stylelintには自動修正の機能も備わっています。そちらを利用して自動修正までやってもらいましょう。webpackの設定からオプションを指定するだけなのでめちゃくちゃ簡単です。

webpack.config.jsnew StylelintPlugin()にオプションを追加します。該当部分だけ抜粋すると以下のとおりです。

webpack.config.js
コピーplugins: [ new StylelintPlugin({ fix: true, }), ]

これで実行してみると、出力されるCSSが自動的に修正されていることが分かるはずです!

dist/css/style.css
コピーbody { background: #eee; } #main p { color: #666; }

ベンダープレフィックスを自動付与

webpackでのSassのコンパイル時に自動でベンダープレフィックスを付与してあげましょう。ベンダープレフィックスの付与といえば、Autoprefixerですね。

PostCSSのプラグインであるAutoprefixerを、postcss-loaderを通して適応させるようなイメージです。

その2つをインストールします。

コピーnpm install --save-dev postcss-loader autoprefixer

webpackの設定

css-loadersass-loaderの間にpostcss-loaderを追加してあげます。PostCSSのプラグインとしてautoprefixerを読み込みオプションで条件を指定します。
(この例ではIE11以上、各ブラウザの過去2バージョンまでサポート)

webpack.config.js
コピーmodule: { rules: [ { test: /\.scss$/i, use: [ { loader: MiniCssExtractPlugin.loader, }, { loader: 'css-loader', }, { loader: 'postcss-loader', options: { plugins: [ require('autoprefixer')({ browsers: [ "last 2 versions", "ie >= 11", ], }), ] } }, { loader: 'sass-loader', options: { sassOptions: { outputStyle: 'expanded', }, }, }, ] }, ] },

ちなみに"browserslist": [ "defaults" ]でデフォルトの指定となります(オプションを指定しない場合も同じ)

ベンダープレフィックス付きそうなCSSを追加してビルド

background-image: linear-gradient()を追加してみました。

src/css/style.scss
コピーbody { background: linear-gradient(to right, #ff6f00 50%, #009688 50%); } #main { p { color: #666; } }

webpackでビルドしてみましょう。

コピーnpx webpack

-webkit-がついてかつ謎の記載が追加されていますね。

dist/css/style.css
コピーbody { background: -webkit-gradient(linear, left top, right top, color-stop(50%, #ff6f00), color-stop(50%, #009688)); background: linear-gradient(to right, #ff6f00 50%, #009688 50%); } #main p { color: #666; }

これでAutoprefixerの導入もできました!

CSS内のURLが解決できない問題を解消する

実は今のままだと、CSS内のbackground: url(../img/hoge.png);のように記載された時のURLの部分でエラーが起きます。これを解消するためにfile-loaderを導入してあげます。

コピーnpm install --save-dev file-loader

webpackの設定

jpg等の拡張子に対して、file-loaderを読み込んであげてください。
module部分の一部を抜粋しています。

webpack.config.js
コピーmodule: { rules: [ { test: /.(jpg|png|gif|svg)$/, use: { loader: 'file-loader', options: { name: '../img/[name].[ext]', } } }, { test: /\.scss$/i, use: [ { loader: MiniCssExtractPlugin.loader, }, { loader: 'css-loader', options: { url: true, }, }, { loader: 'sass-loader', options: { sassOptions: { outputStyle: 'expanded', }, }, }, ] }, ] },

プロパティをソートしたい

PostCSSのプラグインであるcss-declaration-sorterによってwebpackでCSSプロパティをソートすることができます。

コピーnpm install --save-dev css-declaration-sorter

webpackの設定

postcss-loaderのプラグインとして拡張してあげます。
postcss-loaderのサンプル部分だけを提示しています。

webpack.config.js
コピー{ loader: 'postcss-loader', options: { plugins: [ require('autoprefixer')({ browsers: [ "last 2 versions", "ie >= 11", ], }), require('css-declaration-sorter')({ order: 'alphabetical' }), ] } },

指定できる値は以下の3つです。今回はアルファベット順で指定してみました。

  • alphabetical・・・アルファベット順
  • smacss・・・重要なプロパティ順
  • concentric-css・・・ボックスモデルの外から内

プロパティ順を混ぜてビルド

あえてアルファベット順ではないSassファイルをビルドしてみます。

src/css/style.scss
コピーbody { z-index: 1; text-align: center; background: #eee; } #main { p { color: #666; border: 1px solid #666; } }
コピーnpx webpack

ちゃんとアルファベット順にソートされました!

dist/css/style.css
コピーbody { background: #eee; text-align: center; z-index: 1; } #main p { border: 1px solid #666; color: #666; }

メディアクエリを1つにまとめる

Sassでメディアクエリを入れ子で記載した場合、通常通りのコンパイルすると、いたるところにメディアクエリが出てきてしまいます。これをまとめるための拡張を行っていきます。

コピーnpm install --save-dev postcss-sort-media-queries

webpackの設定

PostCSSのプラグインとして追加してあげてください。
postcss-loaderのサンプル部分だけを提示しています。

webpack.config.js
コピー{ loader: 'postcss-loader', options: { plugins: [ require('autoprefixer')({ browsers: [ "last 2 versions", "ie >= 11", ], }), require('css-declaration-sorter')({ order: 'alphabetical', }), require('postcss-sort-media-queries')({ sort: 'desktop-first', }), ] } },

指定できるsortのオプションは以下の2つです。

  • mobile-first・・・スマホファースト
  • desktop-first・・・PCファースト

ビルドを試してみる

以下のような入れ子でメディアクエリを入れたSassを用意してみました。PCファーストです。

src/css/style.scss
コピーbody { background: #eee; @media (max-width: 1079px) { background: #999; } @media (max-width: 767px) { background: #111; } } #main { p { color: #666; @media (max-width: 1079px) { background: #333; } @media (max-width: 767px) { background: #fff; } } }

webpackでビルドしてみます。

コピーnpx webpak

メディアクエリごとに正しくまとまって出力してくれるようになりました!

src/css/style.css
コピーbody { background: #eee; } #main p { color: #666; } @media (max-width: 1079px) { body { background: #999; } #main p { background: #333; } } @media (max-width: 767px) { body { background: #111; } #main p { background: #fff; } }

importをまとめる

通常Sassでimportする場合は以下のようにファイル名まで指定して書きます。

コピー@import "page/index"; @import "page/about"; @import "page/contact";

ただ、これだとSassのファイルが増えるたびにimportの記述を増やす必要があり手間です。globというツールを使えば@import "page/**";という**の記述によってそのフォルダ配下のSassをすべて読み込めるという書き方ができて便利なので、こちらもwebpackに導入していきましょう!

Sassの前にimport-glob-loaderローダーを通すことで、importをファイル名まで指定しなくても読みこんでくれるようになります。

コピーnpm install --save-dev import-glob-loader

そしてwebpackの設定でローダーとして指定してあげてください。ポイントはsass-loaderより前(コード上は下)に配置してあげればOKです!

moduleのサンプル部分だけを提示しています。

webpack.config.js
コピー{ loader: 'sass-loader', options: { sassOptions: { outputStyle: 'expanded', }, }, }, { loader: 'import-glob-loader', },

おわり

webpackでSassをコンパイルする方法でした。もともとGulpを主体でSassをコンパイルして作業していましたがwebpackでも同様のコンパイルが問題なく行えることが分かりました。

ちなみにGulpの記述は以下の記事で紹介しています。

webpackでSassをコンパイルしたい人の助けになれば幸いです。

この記事を書いた人

はにわまん

WordPressが得意なWeb屋。HPcode代表。

300件以上のWordPressカスタマイズを対応してきました。SE → 農家 → アフィリエイター → Web屋。生まれは三重県。