※webpack1系の記事です。
1→2への移行ガイドを見る限り破壊的な変更点はそんなにありませんが、preLoaders
やpostLoaders
がなくなっていたり、差が出ていますのでご注意ください。
https://webpack.js.org/guides/migrating/
webpackを手を動かしつつ学べる初学者向け資料を作成しました。
公式のチュートリアルもあるのですが、webpackが使用されているOSSのボイラープレートを見る限り、世の中の使われ方に沿ってないかも?と思い書きました。
これから始める人の手助けになれば幸いです。
また最終形のソースをupしてあるので詰まったら見てください。
webpackとは
webpack で始めるイマドキのフロントエンド開発の説明が良かったので引用させていただきます。m(_ _)m
webpack は WebApp に必要なリソースの依存関係を解決し、アセット(配布物)を生成するビルドツール(要するにコンパイラ)です。JavaScript だけでなく、CoffeeScript や TypeScript、CSS 系、画像ファイルなどを扱うことができます。
WebApp のビルドツールは Grunt や Gulp が有名です。これらは基本的に、ビルド手順をタスクという形で自ら定義する必要があり、フロントエンド開発に馴染みのない開発者にとっては敷居が高いものでした(少なくとも、自分はそうでした)。
webpack を使えば、Grunt も Gulp も必要ありません!覚えるべきことはほとんどありません。(必要なら)簡単な設定ファイルを書いて webpack コマンドを実行するだけです。
ただWebpackはビルドツールなのでビルド以外のタスクが出てきたらGruntやGulpに任せた方が無難だと思います。
また最近発表されたReact.jsアプリの雛形生成ツールでも使われていますね。
Facebook公式のcreate-react-appコマンドを使ってReact.jsアプリを爆速で作成する
前提
Nodejs,npmが導入済みであること
流れ
Step | 目的 |
---|---|
Step 1 | 準備 |
Step 2 | 簡単なjsでwebpackを実行してみる |
Step 3 | 設定ファイル(webpack.config.js)を導入 |
Step 4 | Loaderを導入 |
Step 5 | Loaderの様々な指定方法 |
Step 6 | 拡張子の省略(resolve.extensions) |
Step 7 | Pluginを導入 |
Step 8 | ソースマップの出力(devtool) |
Step 1 準備
npm init
webpackのインストールも行っておきます。
本記事ではグローバルではなくプロジェクト内にインストールする形にします。
npm install --save-dev webpack
Step 2 簡単なjsでwebpackを実行してみる
このStepでは簡単なjsファイルを作ってwebpackを実行します。
webpackがどのような処理をしているのかを理解しましょう。
cats.jsの作成
猫の一覧を配列で返すJavaScriptです。
var cats = ['tama', 'kuro', 'tora']
module.exports = cats;
app.jsの作成
エントリーポイントとなるメインのJavaScriptです。
猫の一覧を読み込んでログ出力します。
var cats = require('./cats.js');
console.log(cats);
webpackの実行
早速実行してみます。
以下のようにして実行が可能です。
node node_modules/.bin/webpack app.js bundle.js
Hash: 2781572ec422f461e66a
Version: webpack 1.13.1
Time: 64ms
Asset Size Chunks Chunk Names
bundle.js 1.58 kB 0 [emitted] main
[0] ./app.js 51 bytes {0} [built]
[1] ./cats.js 58 bytes {0} [built]
app.jsがwebpackに読み込ませるファイルでbundle.jsが出力するファイル名です。
このapp.jsをエントリーポイントと呼びます。
実行方法の最適化
上で書いたような実行も可能ですが、毎回こんな長いパス打つのは大変です。
package.jsonに書くことでnode_modules/.bin/
にでパスが自動で通るためコマンドをシンプルにできます。
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
+ "build": "webpack app.js bundle.js"
},
npm run build
bundle.jsを実行
作るだけ作って実行はしていませんでした。
作成されたbundle.jsを実行してみましょう。
node bundle.js
[ 'tama', 'kuro', 'tora' ]
cats.jsの中身が表示されていますね。
Step 3 設定ファイル(webpack.config.js)を導入
このStepではwebpack.config.js
という設定ファイルでwebpackの実行時の設定を行うように変更します。
上記の名前でファイルを作成しておくとwebpack
と打つだけでwebpack.config.js
が自動で読み込まれます。
webpack.config.jsへの移行
webpack.config.jsファイルを作成して、以下のようにエントリーポイントと出力ファイル名を記述します。
※エントリーポイントの頭に./
がつくのに注意してください。
webpack.config.jsの作成
module.exports = {
entry: './app.js',
output: {
filename: 'bundle.js'
}
};
package.jsonを修正
package.json内のコマンドをwebpackだけにします。
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
- "build": "webpack app.js bundle.js"
+ "build": "webpack"
},
確認
ビルド&実行して動作を確認してください。
npm run build
node bundle.js
ソースディレクトリ・出力ディレクトリを作成
プロジェクト直下のファイルが多くなってきました。
今後辛くなってくるのでソースディレクトリと出力ディレクトリを分けます。
ディレクトリの作成とファイルの移動
mkdir src
mv app.js src
mv cats.js src
rm bundle.js
webpack.config.jsを修正
module.exports = {
- entry: './app.js',
+ entry: './src/app.js',
output: {
+ path: 'dist',
filename: 'bundle.js'
}
};
確認
ビルド&実行して動作を確認してください。
npm run build
node dist/bundle.js
Step 4 Loaderを導入
このStepではwebpackの肝であるLoaderの概要と基本的な使い方を習得しましょう。
Loaderとは?
LoaderはTypeScriptやBabel、PNGやJPEG、SassやLESS、JSONやYAMLなど様々なファイルを読み込むことが可能になる拡張機能のことです。
JSONを読み込んでみよう
cats.jsは配列を返すJavaScriptでしたね。
これをJSONで読み込むようにしてみましょう。
cats.jsonの作成
※わかりやすくするためcats.json
という猫がいることにします。。
["cats.json", "tama", "kuro", "tora"]
json-loaderのインストール
JSONを読み込むにはjson-loaderというLoaderを使います。
npm install --save-dev json-loader
json-loaderをロードするように設定
cats.jsからcats.jsonに変更し、requireの頭にjson-loader!
を追加します。
- var cats = require('./cats.js');
+ var cats = require('json-loader!./cats.json');
console.log(cats);
また、以下のように -loader
は省略可能です
以後は省略記法で記載していきます。
- var cats = require('json-loader!./cats.json');
+ var cats = require('json!./cats.json');
console.log(cats);
確認
出力にcats.jsonが含まれていることを確認してください。
npm run build
node dist/bundle.js
[ 'cats.json', 'tama', 'kuro', 'tora' ]
json-loaderは何をしている?
実際のjson-loaderのソースを見てみましょう。
module.exports = function(source) {
this.cacheable && this.cacheable();
var value = typeof source === "string" ? JSON.parse(source) : source;
this.value = [value];
return "module.exports = " + JSON.stringify(value, undefined, "\t") + ";";
}
上の方は無視して最後のreturn行を見てください。
"module.exports = "
にJSONを文字列化したもの
つなげてreturnしていますね。
cats.jsと同じようなJavaScriptの文を生成しているのがわかるかと思います。
JSONで書かれたの文字列をJavaScriptで書かれた文字列に変換しているわけですね。
複数のLoaderを扱う
Loader同士は繋げることが可能です。
例としてYAMLファイルを扱えるyaml-loaderを扱います。
cats.ymlの作成
※わかりやすくするためcats.yml
という猫がいることにします。
[cats.yml, tama, kuro, tora]
yaml-loaderのインストール
npm install --save-dev yaml-loader
app.jsの修正
cats.jsonからcats.ymlに変更します。
json!
の後にyaml!
を追加します。
※Loaderは右から左の順に実行されます。
※yaml-loaderはJSONを出力します。
そのため以下の様にLoaderを繋ぐ必要があります。
YAML ⇢ yaml-loader ⇢ JSON ⇢ json-loader ⇢ JavaScript
- var cats = require('json!./cats.json');
+ var cats = require('json!yaml!./cats.yml');
console.log(cats);
確認
出力にcats.ymlが含まれていることを確認してください。
npm run build
node dist/bundle.js
[ 'cats.yml', 'tama', 'kuro', 'tora' ]
つまりLoaderとは
通常、require()はJavaScriptを読み込むためのものですが、Loaderを通すことでJavaScript以外のリソースを読み込めるようにするためのものです。
最終的にJavaScriptの形に変換します。
Step 5 Loaderの様々な指定方法
このStepではLoaderの指定方法の違いを見ていきます。
requireで指定
これは今までに出てきたrequire('hoge-loader!〜)の形です。
module.Loadersで指定
ファイルを読み込む処理が発生するたびにrequireにLoaderを書いていくのは大変です。
そこでデフォルトのLoaderを定義しておくことで記述を省略できます。
設定プロパティは以下の2つが主です。
- testプロパティに対象となるファイルを検索する正規表現を指定
- loaderプロパティに通したいLoaderを指定
.ymlに対してはyaml-laoder->json-loaderで通すのでしたね。
それを行うための設定が以下のようになります。
module.exports = {
entry: './app.js',
output: {
filename: 'bundle.js'
- }
+ },
+ module: {
+ loaders: [
+ {test: /\.yml$/, loader: 'json!yaml'}
+ ]
+ }
};
拡張子で設定することが多いようです。
testに複数マッチするように設定してしまうとと、マッチした全てのloaderを通すことになるので注意です。
上記設定をおこなったので以下の様にrequire部分でloaderを省略が可能です。
- var cats = require('json!yaml!./cats.yml');
+ var cats = require('./cats.yml');
console.log(cats);
module.preLoadersで指定
これは最初に通すLoaderです。
module.postLoadersで指定
これは最後に通すLoaderです。
順番と使い分け
順番としては以下の様になります。
- module.preLoaders(設定ファイル)
- module.Loaders(設定ファイル)
- require内のLoader(ソースコード)
- module.postLoaders(設定ファイル)
基本はmodule.Loadersに設定するといいと思います。
loadersの指定方法について補足
複数loaderの指定
!
で繋げて文字列で指定する書き方の他に配列で指定することも可能です。
loaders: [
{
test: /\.yml$/,
- loader: 'json!yaml'
+ loaders: ['json', 'yaml']
}
]
queryの指定
loaderの種類によってオプションとしてパラメータを受け取れるものがあります。
その場合、文字列ではなくオブジェクトで指定が可能です。
※複数loaderを繋げる場合は文字列で書くしかないかも?
loaders: [
{
test: /\.hoge$/,
- loader: 'hoge?key1=value1'
+ loader: 'hoge'
+ query: { key1: 'value1' }
}
]
Step 6 拡張子の省略(resolve.extensions)
通常requireに指定するファイル名には拡張子をつけますがその省略が可能です。
その設定を行うのがresolve.extensionsです。
拡張子の省略
resolve.extensionsのデフォルト値は
["", ".webpack.js", ".web.js", ".js"]
のようになっており.js
が最初から含まれています。
例えばjsを読み込む場合は、以下のように.jsが省略可能です。
- var cats = require('./cats.js');
+ var cats = require('./cats');
console.log(cats);
順番に注意
extensionsは配列になっており順番が重要です。
見つかるまで前から順に探します。
つまりrequire('./cats')
と指定した場合は以下の流れです。
-
cats
のファイルを探しにいく -> 見つからない -
cats.webpack.js
のファイルを探しにいく -> 見つからない -
cats.web.js
のファイルを探しにいく -> 見つからない -
cats.js
のファイルを探しにいく -> 見つかった! -> require('./cats.js')として扱う。
上書きされるので注意
resolve.extensionsの設定は追加ではなく上書きです。
そのためextensionsの最初の空文字""
を消して[".js"]
だけ指定してしまうと拡張子付きのrequire('./cats.js')という書き方ができなくなってしまいます。
省略しつつYAMLを読み込む
デフォルトでは.yml
は入っていませんでしたね。
.yml
を拡張子なしで読み込んでみましょう。
以下の様にresolve.extenisonを上書きする設定を追加します。
module.exports = {
entry: './src/app.js',
output: {
path: 'dist',
filename: 'bundle.js'
},
+ resolve: {
+ extensions: ["", ".webpack.js", ".web.js", ".js", ".yml"]
+ },
module: {
loaders: [
{test: /\.yml$/, loader: 'json!yaml'}
]
}
};
- var cats = require('./cats.yml');
+ var cats = require('./cats');
console.log(cats);
cats.jsをリネームしてcats.ymlが読み込まれることを確認
cats.jsを読み込ませないためにcats.jsを退避しておき、cats.ymlを読み込まれることを確認しましょう。
mv src/cats.js src/cats.js_bk
npm run build
node dist/bundle.js
[ 'cats.yml', 'tama', 'kuro', 'tora' ]
省略するメリット
省略したらどのファイルを読み込むのかがわかりにくくなる反面、疎結合にできます。
先程の例でいうと呼び元は猫の一覧が欲しいのです。
それがJavaScriptであろうとJSONであろうとYAMLであろうと呼び元のソースコードは変えずにファイルの有無でファイルを切り替えることが可能です。
Step 7 Pluginを導入
webpackはPluginを用いることで機能を拡張する事が可能です。
このstepでは例として、トップページのhtmlを自動生成するPluginと圧縮するプラグインを導入します。
html-webpack-pluginのインストール
htmlを自動生成するためのプラグインです。
npm install --save-dev html-webpack-plugin
設定ファイルを修正
圧縮も一緒に設定してしまいます。
圧縮はwebpack.optimize.UglifyJsPlugin
を使用します。
(圧縮はwebpackの標準プラグインなのでnpmインストールは不要です)
+ var webpack = require('webpack');
+ var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/app.js',
output: {
path: 'dist',
filename: 'bundle.js'
},
resolve: {
extensions: ["", ".webpack.js", ".web.js", ".js", ".yml"]
},
module: {
loaders: [
{test: /\.yml$/, loader: 'json!yaml'}
]
- }
+ },
+ plugins: [
+ new webpack.optimize.UglifyJsPlugin(),
+ new HtmlWebpackPlugin({
+ title: 'Sample Page'
+ })
+ ]
};
※HtmlWebpackPluginにtitleだけ設定していますが、テンプレートHTMLから作ったり様々な機能があります。
htmlが作られたことを確認
npm run build
Hash: 212358e547c6141ea787
Version: webpack 1.13.1
Time: 936ms
Asset Size Chunks Chunk Names
bundle.js 304 bytes 0 [emitted] main
index.html 182 bytes [emitted]
[0] ./src/app.js 48 bytes {0} [built]
+ 1 hidden modules
Child html-webpack-plugin for "index.html":
+ 3 hidden modules
bundle.jsを読み込むhtmlが作られます。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Sample Page</title>
</head>
<body>
<script type="text/javascript" src="bundle.js"></script></body>
</html>
ブラウザでindex.htmlを開いて確認
ブラウザでindex.htmlを開いてみましょう。
consoleにちゃんと猫の一覧が表示されていますね。
またsourcesタブを開いてbundle.jsを見てみると圧縮されていることが確認できます。
Step 8 ソースマップの出力(devtool)
webpackではソースマップの出力の仕組みが元々組み込まれているのでプラグイン等不要で出力可能です。
ソースマップを設定
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/app.js',
output: {
path: 'dist',
filename: 'bundle.js'
},
resolve: {
extensions: ["", ".webpack.js", ".web.js", ".js", ".yml"]
},
module: {
loaders: [
{test: /\.yml$/, loader: 'json!yaml'}
]
},
plugins: [
new webpack.optimize.UglifyJsPlugin(),
new HtmlWebpackPlugin({
title: 'Sample Page'
})
- ]
+ ],
+ devtool: 'source-map'
};
npm run build
ブラウザでindex.htmlを開いて確認
index.htmlを開いてsourcesタブを表示します。
webpack://
の配下に圧縮前のファイルが表示されます。
圧縮前のファイルにもブレークポイントも貼れるのでデバッグも問題ありませんね。
補足:devtoolの設定値
本記事では'source-map'を指定しましたが7種類もあるようです。
https://webpack.github.io/docs/configuration.html#devtool
'source-map'は情報量が多い反面、実行に時間がかかるみたいですね。
'source-map'で遅いと感じたら違うのを検討していく形でしょうか。
あとがき
本記事からさらに学びたい方は公式ドキュメントの使い方の例を見ていくと良いと思います。
webpackはLoaderやPluginの種類が豊富なのでそのへんの逆引きリファレンスみたいなやつの需要あるかもしれないなぁ。