Webpack4とBabelを使うに当たってのテンプレートです。
簡単な説明
Webpackとは
node.jsで動くモジュールバンドラーです。コーディング時はモジュール単位で開発して、ビルド時にCSS、画像を含むすべてのコードを1つのJSにまとめて吐き出すことができます。
今回は、CSS、画像を別々にするパターンも書いてます。
Babelとは
最新のJavaScriptの機能をブラウザのサポートを待たずにして使えるようにするためのトランスパイラです。
たとえばES6で書いたコードをどのブラウザでも動く旧来のコードに変換します。
Webpackと連携して使います。
初期化とインストール
node.jsなのでnpm init
します。
設定に関してはお好みですがエンター連打でいいと思います。
npm init
Webpack関連をインストールします。
開発時に、保存でオートリロードをしたいのでwebpack-dev-server
も入れておきます。
npm install --save-dev webpack webpack-cli webpack-dev-server
webpack.config.js
で使用するプラグイン群をインストールします。
説明は後述します。
npm install --save-dev webpack-merge html-webpack-plugin clean-webpack-plugin
CSSと画像をビルドに含めるためにloader系のモジュールをインストールします。
npm install --save-dev style-loader css-loader url-loader
ES6(ES2015)を使用するためにBabel関連をインストールします。バージョンは7系です。
npm install --save-dev @babel/core @babel/preset-env babel-loader
Webpack Configの用意
コマンドのオプションでも指定できますが、通常はconfigファイルを作成して各種設定を行います。
configの作り方はいろいろあるかと思いますが、基本的に公式ドキュメントに従って開発用と本番用のファイルを作ります。
仕組みとしては共通的な設定ファイルとしてwebpack.common.js
を作成し、開発用と本番用の差分の設定をwebpack.dev.js
とwebpack.prod.js
に記述する形になります。これらのファイルはwebpack-merge
によって自動的にマージされます。
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
app: './dev/js/index.js',
},
plugins: [
new CleanWebpackPlugin(['build']),
new HtmlWebpackPlugin({
title: "Sample HTML"
}),
],
output: {
filename: './js/[name].bundle.js',
path: path.resolve(__dirname, 'build')
},
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
},
{
test: /\.css/,
use: ['style-loader', {
loader: 'css-loader',
options: {
minimize: true,
sourceMap: process.env.NODE_ENV === 'development',
},
}, ],
},
{
test: /\.(gif|png|jpg)$/,
use: [{
loader: 'url-loader'
}]
}]
}
};
-
plugins
-
clean-webpack-plugin
は指定したディレクトリの中身をすべて削除するプラグインです。 -
html-webpack-plugin
は指定したhtmlファイルに<script>
タグを挿入してビルドしたJSを自動的にバインドするプラグインです。template
にhtmlファイルを設定しない場合はindex.html
が勝手に作成されます。独自のhtmlを使う場合は、template:'dev/index.html'
という項目を追加します。
-
-
module
-
babel-loader
の部分はBabelのトランスパイラに関する設定です。 -
style-loader
とcss-loader
はCSSファイルをJSにバンドルのに必要です。 -
url-loader
は画像ファイルをBase64に変換してJSにバンドルします。※あまり大きいサイズの画像には向かない。 -
sourceMap
はソースとビルド結果の紐付けを行い、ブラウザのコンソールでエラー箇所が分かるようになります。今回はdeproyment
の時のみ有効にしてます。
-
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
contentBase: './dist'
}
});
-
devServer
はwebpack-dev-server
の監視対象ディレクトリ。portのデフォルトは8080です。
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'production',
});
-
mode
がproduction
になっているとコンパイル時にコードが圧縮されます。- 自分で圧縮内容を設定する場合は別途
UglifyJS2
のプラグインが必要。
- 自分で圧縮内容を設定する場合は別途
package.json
npm-scriptsを記述して、開発サーバの立ち上げと本番用のビルドが簡単に行えるようにします。
ここで上記の開発と本番の設定ファイルを指定します。
"scripts": {
"start": "NODE_ENV=development webpack-dev-server --open --config webpack.dev.js",
"build": "webpack --config webpack.prod.js"
},
各ファイルの用意
サンプルなので何でもいいですが、以下のようなファイルを用意しました。
jQueryも使ってみます。
npm install jquery --save
import $ from "jquery"
import '../styles/index.css'
import pic from '../images/example.png'
$(() => {
let div = $("<div>").attr({id: "sample"}).text("Sample Text");
$("body").append(div);
let img = new Image();
img.src = pic;
$("body").append(img);
});
#sample {
font-size: 3.0rem;
}
images/example.png
も適当なのを用意する。
最終的なディレクトリ構成図
.
├── dev
│ ├── images
│ │ └── example.png
│ ├── js
│ │ └── index.js
│ └── styles
│ └── index.css
├── node_modules
├── package-lock.json
├── package.json
├── webpack.common.js
├── webpack.dev.js
└── webpack.prod.js
実行してみる
開発モード
npm start
を実行するとhttp://localhost:8080
でブラウザが立ち上がります。
index.js
を開いて"Test Text"
などに書き換えるとブラウザの表示が自動的に書き換わるはずです。
本番用ビルド
npm run build
を実行するとdist
ディレクトリ以下にバンドル済みのファイル群が作成されます。
index.html
をブラウザで開くと上記と同じように"Sample Text"
が表示されるはずです。
CSSと画像を外出しする
JSにマージせずファイルとして独立させたい場合は、以下のモジュールの追加が必要です。
またstyle-loader
とurl-loader
が不要になるので消しときます。
npm install --save-dev mini-css-extract-plugin file-loader
npm uninstall --save-dev url-loader style-loader
webpack.common.js
の以下3点を変更します。
- JSからCSSを除外して別ファイルとして分離するために
MiniCssExtractPlugin
を追加。-
style-loader
は不要なので削除する。
-
- 画像をファイルとして扱うためにfile-loader`を追加。
-
url-loader
は不要なので削除する。
-
-
css-loader
のoptions.url:
はfalse
に変更。url()
を変換しなくなります。falseにないとBase64でアクセスしようとするのでエラーになる。
修正後の全体は以下です。
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: {
app: './dev/js/index.js',
},
plugins: [
new CleanWebpackPlugin(['build']),
new HtmlWebpackPlugin({
title: "Sample HTML"
}),
// MiniCssExtractPluginを追加、cssの出力先を指定
new MiniCssExtractPlugin({
filename: './styles/[name].css',
})
],
output: {
filename: './js/[name].bundle.js',
path: path.resolve(__dirname, 'build')
},
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
},
{
test: /\.css/,
use: [
MiniCssExtractPlugin.loader, // <--追加:CSSをJSから分離する
//'style-loader', <--削除
{
loader: 'css-loader',
options: {
url: false, // <--追加:url()を変換しない
minimize: true,
sourceMap: process.env.NODE_ENV === 'development',
},
},
],
},
// url-loaderを削除してfile-loaderを追加
{
test: /\.(gif|png|jpg)$/,
loader: 'file-loader',
options: {
name: './images/[name].[ext]'
}
}]
}
};
これでnpm run build
するとbuild
以下が分離されるようになります。
├── build
│ ├── images
│ │ └── example.png
│ ├── index.html
│ ├── js
│ │ └── app.bundle.js
│ └── styles
│ └── app.css
その他
直接は関係ないのですが、GitHubとかに上げる時、npmが作成する/node_modules
やビルド結果の/build
が混じっていると面倒なので除外しておくのがいいと思います。
/node_modules
/build
おわりに
- できるだけ簡単な構成にしたかったけど依存モジュールが多くて結構ややこしいことになった。流行り廃りが早いから構成の管理が大変そう。
- SPAだったらindex.htmlだけでいいけど、複数ページに渡る場合はエントリーポイントを分けたりしないといけない。ここらへんが参考になりそう。
- ただその場合、React使うだろうし、Reactを使うなら
create-react-app
でプロジェクト作成すれば上記の設定はほとんど自動で作成されるのでそちらのほうが良い。 - サーバーサイドでhtml生成するならそもそもWebpackは使わない??
- ただその場合、React使うだろうし、Reactを使うなら
参考文献
https://numb86-tech.hatenablog.com/entry/2018/10/24/221130
https://ics.media/entry/17376#procon-css
https://qiita.com/tsuuuuu_san/items/582854a4043d8a1db1c9
http://www.kantenna.com/pg/2018/05/webpack_scss.php