【2019年版】webpack 4 個人的設定まとめとして、新たに再編集しました。最新の設定はこちらの記事にて紹介しています。
フロントエンドに必須技術となってきたwebpack。自分も少しずつ案件に取り入れつつ覚えてきましたが、便利すぎて手放せなくなってきました。最初はJavaScriptのBabelを使うためだけに使っていましたが、欲が出てSASS(SCSS)のビルドもwebpackで行うようになりました。構築環境をまとめると管理や修正が楽ですし、なにより作業工数を削減することもできます。
今回はwebpackによる環境構築から、ファイルの出力までを紹介します。環境はMac(macOS Sierra)、node v10.1.0、npm 6.4.1、webpackのバージョンは4.17以上で進めます(記事アップデート前は4.12)。webpackはデフォルトでcssもなにもかもをまとめて1つのJavaScriptファイルとして生成しますが、環境よってはまだ別に使いたい時もあるので(WordPressのテーマ作成時など)、最終的にJavaScriptファイルとCSSファイルは分けて出力する形になります。もちろん設定次第で一緒にすることも可能です。
注意!2018年8月28日にBabel 7(v7.0.0)がリリースされ、loaderの記述方法なども変更されました。本記事もBabel7に対応し、babel 6.xまでの記述が変更となります。babel-loaderの最新版はBabel7用で、babel 6.x用は「babel-loader@7」になります(ややこしい)。
環境を構築する場所を決める
webpackの環境を構築する場所(ディレクトリ)を決めます。よくmyprojectとか使われますが、私は一目でどの案件かがわかるように名前を決めています。今回はサンプルですのでmyprojectを使わせてもらいます。
ローカルの任意の場所にmyprojectを構築するディレクトリ(フォルダ)を生成します。新規フォルダの作成でもいいですが、コマンドラインで作った方がその後の流れ的に楽です。
コマンドライン
mkdir myproject
webpackをインストール
webpackはグローバルではなくプロジェクト毎にインストールします。インストールは先ほど作成したmyprojectに移動して行います。
コマンドライン
cd myproject
npmコマンドでパッケージを管理するJSONファイルを作成します。解説はこちら(英語)。
npm init -y
JSON(package.json)
次のようなpackage.jsonが作成されます。nameなどが自動で書き込まれます。
{
"name": "myproject",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
どこかに公開予定がなければとりあえずnameとscriptsだけでいいです。後からどんどん行が増えていきますが「,(カンマ)」は必要な時にだけ使います。余分なカンマがあるとエラーになるので注意が必要です。
{
"name": "myproject",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
}
}
コマンドライン
コマンドラインに戻り、次のコマンドでwebpackをインストールします。ここではwebpackとwebpack-cli(コマンドラインツール)を一緒にインストールします。「-D」のDは大文字です。小文字で「-d」ではありません。
npm i -D webpack webpack-cli
webpackのインストールが成功するとnode_modulesやpackage-lock.jsonが追加されます。package.jsonにもdevDependenciesという項目が自動で追加され、その中にwebpackが書き込まれています。
JSON(package.json)
{
"name": "myproject",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"devDependencies": {
"webpack": "^4.17.2",
"webpack-cli": "^3.1.0"
}
}
webpack.config.jsを作成する
webpack.config.jsというJavaScriptやCSSなどの各ファイルを結びつけるための処理を記述する設定ファイルを作成します。まずは起点となるJavaScriptファイルへのパスを繋げます。今回はmain.jsにしています。
JavaScript(webpack.config.js)
module.exports = {
entry: './src/js/main.js'
};
次に各ファイルをまとめたJavaScriptファイルを出力する名前と場所を決めます。今回の設定では、distのディレクトリ内にbundle.jsが生成されるようになります。ディレクトリも出力されるファイルも任意の名前に変更できます。
const path = require('path');
module.exports = {
entry: './src/js/main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
}
};
webpackを試してみる
ここまでで設定したwebpackを試してみます。ここからさらにいろいろな設定を加えていくので、こまめにチェックしましょう。確認用にJavaScriptファイルを2つ用意します。
JavaScript(main.js)
import bar from './bar';
bar();
JavaScript(bar.js)
export default function bar() {
// webpack公式サイト https://webpack.js.org/
// Check用なので中身はなんでもOK
}
コマンドライン
コマンドラインでnpx webpackを走らせます。気をつけて欲しいのは、ここで入力するのはnpmではなくnpxです。NSXでもありません、npxです。
npx webpack
コマンドライン(出力結果例・中略)
成功するとwebpack.config.jsで設定したdistディレクトリにbundle.jsが生成されます。が、WARNING in configurationと出て、設定を見直せ!と怒られます。
WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/
JavaScript(webpack.config.js)
WARNINGの指示に従ってwebpack.config.jsを修正します。modeの設定をdevelopmentかproductionにしろとの指示なので、開発段階ではdevelopmentに設定します。productionにするとJavaScriptファイルを圧縮してくれるので公開時は忘れずproductionに設定しましょう。
const path = require('path');
module.exports = {
mode: 'development',
entry: './src/js/main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
}
};
コマンドライン
modeを設定して再度npx webpackを走らせます。
npx webpack
今度はWARNINGが出ずにbundle.jsが生成されます。
Version: webpack 4.17.2
Time: 59ms
Built at: 2018-09-05 15:51:15
Asset Size Chunks Chunk Names
bundle.js 4.6 KiB main [emitted] main
Entrypoint main = bundle.js
[./src/js/bar.js] 99 bytes {main} [built]
[./src/js/main.js] 63 bytes {main} [built]
ここまでがwebpackの基本的な使い方です。bundle.jsの出力先を変更したい場合は、webpack.config.jsのoutput項目を変更します。
Babel用の設定を追加する
ES2018やJSXをなどを使いたいので、webpackにモジュールと設定を追加します。Babel公式サイト(英語)はこちら。
コマンドライン
webpackとwebpack-cliはもうすでに入っている想定です。残りのBabelに関するモジュールを追加します。なおBabel7よりnpmからのインストール方法が変わりました。
npm i -D @babel/core @babel/preset-env babel-loader
JSON(package.json)
package.jsonは以下のようになります。
{
"name": "myproject",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-loader": "^8.0.2",
"webpack": "^4.17.2",
"webpack-cli": "^3.1.0"
}
}
またここからは毎回npxコマンドを入力するのは億劫なので、npxではなく、watchが使えるnpmコマンドに切り替えていくので、package.jsonのscripts部分を修正します。
{
"name": "myproject",
"scripts": {
"build": "webpack",
"watch": "webpack --watch"
},
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-loader": "^8.0.2",
"webpack": "^4.17.2",
"webpack-cli": "^3.1.0"
}
}
JavaScript(webpack.config.js)
モジュールを追加したらwebpack.config.jsも修正します。moduleの項目を新たに追加し、その中にBabelの設定を記述していきます。
const path = require('path');
module.exports = {
mode: 'development',
entry: './src/js/main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
// babel-loaderの設定
rules: [
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env'
]
}
}
],
exclude: /node_modules/,
}
]
}
};
コマンドライン
モジュールを追加し、webpack.config.jsの設定も完了したらコマンドラインでエラーが出ないかチェックしましょう。今回からnpxではなくnpmコマンドになっています。
npm run build
常時監視したい場合は、watchを使います。watchを使うとentryにひもづくファイルが変更される度に自動的にbundle.jsを生成してくれます。
npm run watch
常時監視(watch)を止めたい場合は、controlキー + Cで停止します。
CSSを取り込む設定を追加する
webpackにCSSも取り込みたいのでモジュールと設定を追加します。
npm i -D css-loader style-loader
JSON(package.json)
CSS関連のモジュールを追加した直後のpackage.jsonは以下のようになります。
{
"name": "myproject",
"scripts": {
"build": "webpack",
"watch": "webpack --watch"
},
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-loader": "^8.0.2",
"css-loader": "^1.0.0",
"style-loader": "^0.23.0",
"webpack": "^4.17.2",
"webpack-cli": "^3.1.0"
}
}
JavaScript(webpack.config.js)
モジュールを追加したらwebpack.config.jsも修正します。
const path = require('path');
module.exports = {
mode: 'development',
entry: './src/js/main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
// babel-loaderの設定
rules: [
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env'
]
}
}
],
exclude: /node_modules/,
},
// CSS取り込み設定
{
test: /\.css/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
url: false
}
}
],
}
]
}
};
CSS(main.css)
取り込みたいCSSファイルを作成します。ディレクトリはsrc内にcssディレクトリを新たに作成します。
@charset "UTF-8";
body {
background: yellow;
}
JavaScript(main.js)
main.jsにもCSSを読み込むimport文を記述します。
import '../css/main.css';
コマンドライン
上記設定でbuildを行うとCSSファイルを取り込んだbundle.jsが生成されます。
npm run build
SASSを取り込む設定を追加する
webpackにSASSを取り込む場合のモジュールと設定を追加します。loaderの他にnode-sassもインストールします。
npm i -D sass-loader node-sass
JSON(package.json)
SASS関連のモジュールを追加した直後のpackage.jsonは以下のようになります。
{
"name": "myproject",
"scripts": {
"build": "webpack",
"watch": "webpack --watch"
},
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-loader": "^8.0.2",
"css-loader": "^1.0.0",
"node-sass": "^4.9.3",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.0",
"webpack": "^4.17.2",
"webpack-cli": "^3.1.0"
}
}
JavaScript(webpack.config.js)
モジュールを追加したらwebpack.config.jsも修正します。CSSとは記述が異なるので注意が必要です。
const path = require('path');
module.exports = {
mode: 'development',
entry: './src/js/main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
// babel-loaderの設定
rules: [
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env'
]
}
}
],
exclude: /node_modules/,
},
// SASS取り込み設定
{
test: /\.scss/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
url: false
},
},
{
loader: 'sass-loader',
options: {
sourceMap: true,
}
}
]
}
]
}
};
SASS(main.scss)
取り込みたいSASSファイルを作成します。ディレクトリはsrc内にscssディレクトリを新たに作成します。
@charset "UTF-8";
$bg-color: yellow;
html {
body {
background: rgba( $bg-color, 1 );
}
}
JavaScript(main.js)
main.jsにもSASSを読み込むimport文を記述します。
import '../scss/main.scss';
コマンドライン
上記設定でbuildを行うとSASSファイルを取り込んだbundle.jsが生成されます。
npm run build
JavaScriptとCSSを別々に出力したい
今までの設定ではJavaScriptとCSS(SASS)が一緒になったbundle.jsが生成されますが、ファイルを分けたいという状況があると思います。ここからはさらに踏み込んだ設定を行なっていきます。
JavaScriptをCSSを別々に出力するためのモジュールをインストールします。extract-text-webpack-pluginをインストールしますが、記事執筆時の最新版(v3)ではwebpack v4に対応しておらず、beta版を使用することになります。
webpack v4に対応しているmini-css-extract-pluginをインストールします。
npm i mini-css-extract-plugin
なお、mini-css-extract-plugin使用時にstyle-loaderは必要ないので、アンインストールします。
npm uninstall style-loader
JSON(package.json)
extract-text-webpack-pluginを追加した直後のpackage.jsonは以下のようになります。
{
"name": "myproject",
"scripts": {
"build": "webpack",
"watch": "webpack --watch"
},
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-loader": "^8.0.2",
"css-loader": "^1.0.0",
"node-sass": "^4.9.3",
"sass-loader": "^7.1.0",
"webpack": "^4.17.2",
"webpack-cli": "^3.1.0"
},
"dependencies": {
"mini-css-extract-plugin": "^0.4.2"
}
}
JavaScript(webpack.config.js)
モジュールを追加したらwebpack.config.jsも修正します。constによる変数部分とplugins項目も追加されているので忘れず記述してください。出力ファイルもpluginsで行います。今回の出力ファイル名はstyle.cssにしています。
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
mode: 'development', //development or production
entry: './src/js/main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
// babel-loaderの設定
rules: [
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env'
]
}
}
],
exclude: /node_modules/,
},
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
url: false,
minimize: true,
}
},
{
loader: 'sass-loader'
}
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'style.css',
})
]
};
コマンドライン
上記設定でbuildを行うとbundle.jsとSASSがコンパイルされたstyle.cssが生成されます。
npm run build
CSSが出力される場所はwebpack.config.jsのoutput項目に準じます。cssフォルダを作ってその中に出力したい場合は、pluginsで次のように記述します。
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
//中略
},
plugins: [
new MiniCssExtractPlugin({
filename: '/css/style.css'
})
]
};
【おまけ】jQueryも一緒にbundleしたい
かなり長い記事になってきたのでそろそろ分割した方が良いのかもと思いつつ、まだまだお世話になるjQueryも一緒にbundleする方法を紹介します。分割はそのうち。
npm i jquery
JSON(package.json)
jQueryを追加した直後のpackage.jsonは以下のようになります。
{
"name": "myproject",
"scripts": {
"build": "webpack",
"watch": "webpack --watch"
},
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-loader": "^8.0.2",
"css-loader": "^1.0.0",
"node-sass": "^4.9.3",
"sass-loader": "^7.1.0",
"webpack": "^4.17.2",
"webpack-cli": "^3.1.0"
},
"dependencies": {
"jquery": "^3.3.1",
"mini-css-extract-plugin": "^0.4.2"
}
}
JavaScript(webpack.config.js)
モジュールを追加したらwebpack.config.jsも修正します。またconstで変数を追加するのと、pluginsにjQueryの「$」と「jQuery」がwebpack内で使えるように設定を行います。
const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
// 中略
plugins: [
new MiniCssExtractPlugin({
filename: 'style.css',
}),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
})
]
};
【おまけ2】entryとoutputで入出力を制御する
案件ごとにwebpackの環境を作るとは書きましたが、全く同じ環境(webpack.config.jsなど)を使う状況もあると思います。例えば生成されるbundle.jsをPC版とSP版(モバイルなど)で別々にしたい場合、以下のように設定します。
JavaScript(webpack.config.js)
const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
mode: 'development', //development or production
entry: {
pc: `./src/js/pc.js`,
sp: `./src/js/sp.js`
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name]/bundle.js'
},
module: {
// 中略
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name]/style.css',
})
]
};
PCとSPのJavaScriptファイルが異なる場合を想定し、entry項目を分割しています。それぞれのファイルはoutputの[name]部分でPC及びSPディレクトリにbundle.jsが出力されます。もちろんCSSもplugins項目で同じく[name]設定をすれば別々に出力されます。
冒頭でも書きましたがwebpackはフロントエンドには欠かせない技術となってきています。使い方はまだまだあるので、今後も紹介していきます。