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は以下のように配置しています。
<!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-loader
とsass-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-loader
とsass
のインストール。
npm install --save-dev sass-loader sass
webpackの設定
ローダーの指定は、webpack.config.jsのmodule
に書いていきます。
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を読み込ませます。
import './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をローダーに追加
全体のコードがこちらです。
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を読み込み
コードの全体はこちら。
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を作成して、以下のようにルールを定義してみました。
{
"rules": {
"no-extra-semicolons": true,
"color-hex-length": "short",
}
}
「セミコロンは1個だけにしましょう」と、「16進数の値はできるだけ短く」という指定です。そしてあえてミスとなるようにstyle.scssを書き換えました。
body {
background: #eee;;
}
#main {
p {
color: #666666;
}
}
ビルドして実行結果を確認する
それでは、ビルドして結果を確認してみましょう!
npx webpack
しっかりエラーとして出力され、stylelintが機能していることが分かります。
stylelintに自動修正してもらう
stylelintには自動修正の機能も備わっています。そちらを利用して自動修正までやってもらいましょう。webpackの設定からオプションを指定するだけなのでめちゃくちゃ簡単です。
webpack.config.jsのnew StylelintPlugin()
にオプションを追加します。該当部分だけ抜粋すると以下のとおりです。
plugins: [
new StylelintPlugin({
fix: true,
}),
]
これで実行してみると、出力される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-loader
とsass-loader
の間にpostcss-loader
を追加してあげます。PostCSSのプラグインとしてautoprefixer
を読み込みオプションで条件を指定します。
(この例ではIE11以上、各ブラウザの過去2バージョンまでサポート)
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()
を追加してみました。
body {
background: linear-gradient(to right, #ff6f00 50%, #009688 50%);
}
#main {
p {
color: #666;
}
}
webpackでビルドしてみましょう。
npx webpack
-webkit-
がついてかつ謎の記載が追加されていますね。
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
部分の一部を抜粋しています。
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
のサンプル部分だけを提示しています。
{
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ファイルをビルドしてみます。
body {
z-index: 1;
text-align: center;
background: #eee;
}
#main {
p {
color: #666;
border: 1px solid #666;
}
}
npx webpack
ちゃんとアルファベット順にソートされました!
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
のサンプル部分だけを提示しています。
{
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ファーストです。
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
メディアクエリごとに正しくまとまって出力してくれるようになりました!
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
のサンプル部分だけを提示しています。
{
loader: 'sass-loader',
options: {
sassOptions: {
outputStyle: 'expanded',
},
},
},
{
loader: 'import-glob-loader',
},
おわり
webpackでSassをコンパイルする方法でした。もともとGulpを主体でSassをコンパイルして作業していましたがwebpackでも同様のコンパイルが問題なく行えることが分かりました。
ちなみにGulpの記述は以下の記事で紹介しています。
webpackでSassをコンパイルしたい人の助けになれば幸いです。