webpackでは、JSファイルだけでなくCSSやSASSもバンドルの対象にできる。
このエントリではその方法を書いていく。
webpackのバージョンは以下。
- webpack@4.0.1
- webpack-cli@2.0.9
下準備
webpackをインストール。
$ npm i -D webpack webpack-cli
npm scripts
。
出力されたファイルの中身を見たいので、モードはdevelopment
にしておく。
"scripts": { "build": "webpack --mode development" },
webpack.config.js
。
このように書いてビルドすると、src/index.js
をエントリポイントとして、dest/bundle.js
として出力される。
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dest'), }, }
src/index.js
に次のように書いてみると、dest/bundle.js
が生成される。
const body = document.querySelector('body'); body.innerHTML += 'Hello World!';
// 一部省略 /***/ "./src/index.js": /*!**********************!*\ !*** ./src/index.js ***! \**********************/ /*! no static exports found */ /***/ (function(module, exports) { eval("const body = document.querySelector('body');\nbody.innerHTML += 'Hello World!';\n\n\n//# sourceURL=webpack:///./src/index.js?"); /***/ }) /******/ });
dest/index.html
を作ってdest/bundle.js
を読み込むとHello World!
と表示されるが、これに以下のスタイルをあてるのが、今回の目標。
/* ./src/style.css */ body { color: red; }
css-loader
webpackでは、Loader
を使うことで、JavaScript以外のものでもJavaScriptで扱えるようになる。
CSS用にはcss-loader
があるので、インストールする。
https://github.com/webpack-contrib/css-loader
$ npm i -D css-loader
webpack.config.js
を編集。
module
の部分が、Loader
の設定。CSSファイルにはcss-loader
を使うように設定している。
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dest'), }, module: { rules: [ {test: /\.css$/, use: ['css-loader']} ], }, }
src/index.js
でCSSを読み込むようにしてビルドしてみると、スタイルシートの中身を読み込めていることが分かる。
import css from './style.css'; console.log(css.toString()); // body { // color: red; // }
つまり、src/style.css
の内容も含めて、dest/bundle.js
にバンドルされている。
// bundle.js // 一部省略 /***/ "./src/style.css": /*!***********************!*\ !*** ./src/style.css ***! \***********************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { eval("exports = module.exports = __webpack_require__(/*! ../node_modules/css-loader/lib/css-base.js */ \"./node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.i, \"body {\\n color: red;\\n}\\n\", \"\"]);\n\n// exports\n\n\n//# sourceURL=webpack:///./src/style.css?");
しかしこのような形で読み込めても、実際にスタイルとして使うことは出来ない。
この段階でdest/index.html
を表示しても、スタイルは反映されていない。
dest/index.html
を開いた際にスタイルシートとして読み込まれるようにする必要がある。
style-loader
そこで利用するのが、style-loader
である。
https://github.com/webpack-contrib/style-loader
公式によれば、スタイルタグを使うことでCSSが利用可能になるらしい。
Adds CSS to the DOM by injecting a style tag
$ npm i -D style-loader
webpack.config.js
にも追加。
module: { rules: [ {test: /\.css$/, use: ['style-loader', 'css-loader']} ], },
これでビルドすると、src/index.js
でimport
したスタイルをそのままdest/index.html
で読み込むことができ、文字が赤くなる。
ちなみに、先程src/index.js
の中でimport css from './style.css';
と書いたが、これは動作確認のためであって、実際にはcss
という変数を参照する必要はないため、以下のように書けばよい。
import './style.css';
extract-text-webpack-plugin
2018/10/24 追記
extract-text-webpack-plugin
は2018年4月にドキュメントが更新され、webpack@4
での使用が非推奨になった。
webpack@4
では mini-css-extract-plugin の使用が推奨されている。
numb86-tech.hatenablog.com
追記終わり
style-loader
を使わず、extract-text-webpack-plugin
を使うという方法もある。
https://github.com/webpack-contrib/extract-text-webpack-plugin
これは、Loader
で変換した結果をテキストとしてファイルに抽出するプラグイン。
ちなみに、2018.2.28現在の最新バージョンである3.0.2
はwebpack@4に対応していないので、プレリリースである4.0.0
をインストールする必要がある。
$ npm i -D extract-text-webpack-plugin@next
webpack.config.js
を編集。
const path = require('path'); const ExtractTextPlugin = require("extract-text-webpack-plugin"); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dest'), }, module: { rules: [ {test: /\.css$/, use: ExtractTextPlugin.extract({use: 'css-loader'})} ], }, plugins: [ new ExtractTextPlugin('style.css'), ], }
この状態でビルドすると、dest/style.css
が生成される。
body { color: red; }
その一方で、dest/bundle.js
には、CSSの内容が含まれなくなる。
そのため、dest/index.html
のなかでlink
タグを使ってdest/style.css
を読み込むようにする。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="./style.css"> <title>Learn CSS</title> </head> <body> <script src="./bundle.js"></script> </body> </html>
スタイルシートのなかで画像ファイルを使う
スタイルシートのなかで画像を読み込んでいる場合、その画像もwebpackで出力することになるため、そのための対応が必要になる。
body { color: red; background-image: url('./images/background-image.png'); }
画像を変換するためのLoader
が必要なのだが、それを用意せずにビルドしようとしても、エラーになる。
ERROR in ./src/images/background-image.png Module parse failed: Unexpected character '�' (1:0) You may need an appropriate loader to handle this file type.
この対応方法も、いくつか種類がある。
file-loader
まず、file-loader
を使うパターン。
https://github.com/webpack-contrib/file-loader
これを使うことで必要な画像が出力され、その画像へのURLが、出力されたスタイルシートに記述される。
webpack.config.js
を編集して、.png
に対してfile-loader
を使うようにする。
module: { rules: [ { test: /\.css$/, use: ExtractTextPlugin.extract({ use: 'css-loader', }) }, { test: /\.png$/, use: 'file-loader', }, ], },
これでビルドすると、以下のdest/style.css
が出力されると同時にdest/f20a9eae72f4385000603750517663e5.png
というファイルも出力されるため、画像を読み込めるようになる。
body { color: red; background-image: url(f20a9eae72f4385000603750517663e5.png); }
url-loader
file-loader
だと、新しくファイルを出力することになる。
url-loader
を使うと、ファイルは出力せず、スタイルシートのなかにDataURLとして出力される。
https://github.com/webpack-contrib/url-loader
file-loader
ではなくurl-loader
を使うようにすると、以下のようなスタイルシートが出力される。
body { color: red; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAP0lEQVQ4T2OcOXPmfwYiwL1794hQxcDAOGogznAaDUOcQTMEkk15eTlROUVJSYm4nDJqIM5wGg1D3Dll0CcbAMflUeHimVpmAAAAAElFTkSuQmCC); }
スタイルシートで読み込む画像ファイルはwebpackで出力しない
file-loader
にしろurl-loader
にしろ、src/
に画像を入れておき、ビルドの結果dest/
に画像やDataURLが出力される。
つまり、同じデータが二重に存在することになる。
それを避ける方法として、スタイルシートで読み込む画像は最初からdest/
に用意しておき、webpackの対象外にするという方法がある。
まず、src/
に画像を置くのではなくdest/images/background-image.png
を用意する。
src/style.css
の内容は変えない。
body { color: red; background-image: url('./images/background-image.png'); }
この状態でビルドしようとすると、src/images/background-image.png
を参照しようとして、ファイルが見つからずエラーになる。
css-loader
のオプションでURLの解決を無効にすることで、この問題を解決できる。
module: { rules: [ { test: /\.css$/, use: ExtractTextPlugin.extract({ use: {loader: 'css-loader', options: {url: false}}, }) }, ], },
こうすると、url
の部分はsrc/style.css
に記述したものがそのままdest/style.css
に出力されるため、dest/
にある画像を参照できる。
body { color: red; background-image: url('./images/background-image.png'); }
複数のスタイルシート
複数のスタイルシートをimport
した場合。
import './style.css'; import './style-font-size.css';
以下のようなCSSが出力される。
body { color: red; } body { color: orange; font-size: 30px; }
重複部分を上手く調整したりはしない。ただ単に後からimport
したものが反映される。
css-loader の minimize
minimize
をtrue
にすると、出力結果が圧縮される。
module: { rules: [ { test: /\.css$/, use: ExtractTextPlugin.extract({ use: {loader: 'css-loader', options: {minimize: true}}, }) } ], }, plugins: [ new ExtractTextPlugin('style.css'), ],
body{color:red}body{color:orange;font-size:30px}
SASS
SASS(SCSS)を使いたい場合は、sass-loader
を使う。
https://github.com/webpack-contrib/sass-loader
node-sass
が必須なので、それも一緒にインストールする。
$ npm i -D node-sass sass-loader
複雑なことは何もなく、SASSをCSSに変えてしまい、後はこれまで書いてきた方法でCSSを処理すればいい。
だから、Loader
を使って最初にSASSをCSSに変換してしまえばよい。
まず、src/style.scss
を定義する。
body { color: red; background-image: url('./images/background-image.png'); }
そしてこれを、JSファイルのなかで読み込む。
import './style.scss';
最後に、webpack.config.js
を編集する。
module: { rules: [ { test: /\.scss$/, use: ExtractTextPlugin.extract({ use: [{loader: 'css-loader', options: {url: false}}, 'sass-loader'], }) }, ], },
Loader
の対象を.scss
にする。
そしてまずsass-loader
でCSSに変換し、それをcss-loader
で変換、最後にextract-text-webpack-plugin
を使っている。
Loader
は後ろに書いたものから実行されることに注意。
これで、dest/
にスタイルシートが出力される。
sass-loader
で変換した後はCSSとして扱われるので、extract-text-webpack-plugin
ではなくstyle-loader
を使うことももちろん出来る。