はじめに
0. 感想
-
Webpackは簡単にいうとフロントエンドのソースコード管理ツールで、CSSやJS等のコードを圧縮または変換する。(SASS → CSS, ECMAScript2015 → JavaScriptなど)
- Railsエンジニアからすると、Asset Pipelineとやっていることは近い。
-
↑を行うために、様々な ローダー や プラグイン というものを使用する。
概要
1. webpackとは?
- JS, CSS, 画像などを一つにまとめるためのツール。
- モジュールバンドラーと呼ぶ。
- バンドル ≒ ビルド
- モジュールバンドラーと呼ぶ。
webpack以前
- 各.htmlファイルで
<script>
要素を列挙していた。- 以下のような問題点があった。
-
<script>
要素は順番に実行されるので、並び順に注意を払う必要がある。 - ライブラリ同士やアプリ本体のコードが互いに依存関係にあるような状況が発生してしまう。
-
- 以下のような問題点があった。
<script src='lib.js'></script>
<script src='app.js'></script>
webpack以降
- モジュールバンドラーの利用によって、以下のようなメリットがあった
- 自動的に依存性を解決する
- 一つのファイルにバンドルしてくれるので、HTML側でコードの依存関係を意識することがなく、まとめられたファイルをインポートさえすれば良くなった。
- リクエストの回数を抑える
- 複数ファイルを一つにまとめることで、ブラウザ/サーバー間のリクエスト数を減らすことができた。
- 大規模な開発に向いている
- コードをクラス/関数の単位でモジュール(ファイル)として細かく分割できるので、ここのファイルの見通しが改善され、名前空間を明確に分離できるようになった。
- 自動的に依存性を解決する
2. webpackの仕様
2-1. さまざまなモジュール形式を解釈できる。
- CommonJS
-
module.exports.add = require('hoge')
みたいなやつ。
-
- AMD
- ES Modules
2-2. ローダー/プラグインが豊富。
- ローダー
- リソースを読み込むためのライブラリ。
- 画像やスタイルシートなどをバンドルすることが可能。
- TypeScriptなどのaltJSコンパイルした上でバンドルすることが可能。
- リソースを読み込むためのライブラリ。
- プラグイン
- webpackそのものの拡張。
- コードの圧縮(minify)。
- バンドルしたファイルを実行するためのページの生成。
- webpackそのものの拡張。
タスクランナー・ビルドツール
- Grunt, gulpなどがある。
2-3. フレームワークでの採用実績が増えている。
- Angular, React, Vue.js などのJavaScriptフレームワークのコマンドラインツールでは、内部的にwebpackが利用されている。
3. モジュールバンドラーの基本
-
「モジュールを束ねる」という性質上、モジュールの理解が必須。
- JavaScriptにおけるモジュールとは、即ちファイルのこと。
- ファイル名がそのままモジュール名を意味する。
- JavaScriptにおけるモジュールとは、即ちファイルのこと。
-
export が指定されていないメンバー(変数/関数/クラス)は、モジュールの外からは参照できない
- これによって名前の競合を防げる。
-
import/export を全てのブラウザーで認識できるわけではないため、モジュール間の依存関係を解釈し、一つに束ねるのがモジュールバンドラーwebpack。
4. webpackを利用する
4-1. Node.jsのインストール
- webpackは、Node.js上で動作するコマンドラインツール。
- webpackを利用した開発では、アプリをNode.jsプロジェクトで管理するのが一般的。
4-2. package.jsonの作成
- package.jsonはNode.jsの設定ファイル。
- アプリの基本情報、プロジェクト固有のコマンド、利用しているパッケージ(ライブラリ)などを管理する。
-
npm init -y
というコマンドによって生成される。
4-3. webpackのインストール
Node.jsプロジェクトにwebpackをインストールする。
-
npm install --save-dev webpack webpack-cli
-
--save-dev
オプションは、インストールするパッケージの情報をpackage.jsonに記録しなさい、という意味。--save
オプションでインストールされたパッケージは、 package.json内のdependencies
ブロックに記録される。- package.jsonでパッケージ情報をまとめておくことで、あとから別の環境で必要なライブラリを準備したい場合にも
npm install
コマンド一つで再現できる。-
--save-dev
オプションは、アプリ開発で利用するツールをインストールする場合に利用する。--save
オプションはアプリそのものの実行で利用する。
-
- package.jsonでパッケージ情報をまとめておくことで、あとから別の環境で必要なライブラリを準備したい場合にも
- インストールに成功すると、プロジェクトルートに
/node_modules
フォルダーが生成され、配下にパッケージ本体(ライブラリ)が保存される。
-
5. webpackの基本
-
/dist/index.html
- JavaScriptファイルを呼び出すためのページ
-
/src/index.js
- 各モジュールを呼び出し、実行するコード。エントリーポイント。
- index.htmlがindex.jsを参照する。
- 各モジュールを呼び出し、実行するコード。エントリーポイント。
-
npx webpack
- プロジェクトローカルにインストールされたパッケージのコマンドを実行。
- webpacの既定では、/src/index.jsがエントリーポイントとなり、バンドルの結果は/dist/main.jsに出力される。
- 生成されたmain.jsには
- モジュールを解決するためのコード
- エントリーポイント
- エントリーポイントが依存するコード
- が一式含まれており、ページからは最終的な出力ファイル(main.js)をインポートするだけでアプリを実行できる。
<script src='./main.js'></script>
- が一式含まれており、ページからは最終的な出力ファイル(main.js)をインポートするだけでアプリを実行できる。
- 生成されたmain.jsには
- webpacの既定では、/src/index.jsがエントリーポイントとなり、バンドルの結果は/dist/main.jsに出力される。
- プロジェクトローカルにインストールされたパッケージのコマンドを実行。
6. 設定ファイルの基本
- 実際の開発では、#5 で述べたような規定の動作だけでは不足で、「入出力先を変更したい」「ビルドにあたって任意の処理を追加したい」「そもそも処理対象のファイルを限定したい」など、細やかな挙動を指示する必要が出てくる。
- これらの挙動は
npx webpack
コマンドのオプションとして指定することもできるが、あとで繰り返し実行することを考えれば、設定ファイルとしてまとめておくのが便利かつ一般的。
- これらの挙動は
6-1. 設定ファイルの骨組み
- エントリーポイントと出力先ファイルを設定ファイルで指定し、ビルドする。
// webpack.config.js
// 設定ファイルの外枠
module.exports = {
// エントリーポイント
entry: './src/index.js',
output: {
// 出力先のフォルダ
path: `${__dirname}/dist`,
// 出力先のファイル名
filename: 'main.js'
},
};
> npx webapck --config webpack.config.js
コマンドを実行後、dist/main.jsにエントリーポイントをビルドした内容が出力されていれば、ビルドは成功。
ビルドコマンドのショートカット
- package.json内で設定できる。
...
'script': {
'build': 'webpack --config webpack.config.js'
}
...
// 以下は同じ
> npm run build
> npx webpack --config webpack.config.js
6-2. 開発サーバーの導入
- コードを修正する度にビルドコマンドを再実行するのは面倒。
- webpack-dev-serverを使って自動で再ビルド、ブラウザーのリロードを実行できる。
webpack-dev-serverのインストール
> npm install --save-dev webpack-dev-server
// webpack.config.js
module.exports = {
entry: './src/index.js',
output: {
...
},
devSerevr: {
contentBase: './dist' // コンテンツの基底パス(検索先)の宣言
},
};
// package.json
'script': {
'start': 'webpack-dev-server --open', // --open: サーバー起動時にブラウザを開く
'build': 'webpack --config webpack.config.js'
},
> npm start // npm run startとどちらでも良い。
開発サーバーによるビルドについて
- 開発サーバーは、ビルドの結果をメモリー上で管理している。
- よって、元からある/dist/main.jsには再ビルドの結果は反映されない。ビルドの結果をファイルに保存したい場合には、
npm run build
コマンドを利用する。
- よって、元からある/dist/main.jsには再ビルドの結果は反映されない。ビルドの結果をファイルに保存したい場合には、
watchモードによるファイルの監視
コードの編集時に再コンパイルするだけならば、webpack-dev-serverではなく、webpack本体のwatchモードを利用してもよい。
// package.json
{
'name': 'basic',
...
'build': 'webpack --watch --config webpack.config.js',
...
}
-
npm run build
コマンドを実行し、適当なコードを変更してみると、確かに自動的に再ビルドが実施されることが確認できる。- 一般的には、開発サーバーを利用した方がブラウザーまで制御できるため便利。
- ビルド結果をファイルできちんと確認したいなどの状況では、watchモードは重宝される。
- 一般的には、開発サーバーを利用した方がブラウザーまで制御できるため便利。
本番/開発モードの選択
-
webpack.config.jsで
mode
パラメータを指定する。- webpack4では必須。
-
開発モード
- よりデバッグに適したコードを高速に生成する
-
本番モード
- 不要なコードを削除し、できるだけサイズが小さく(minificationされる)、実行効率の良いコードを生成する。
ソースマップの生成
-
ビルド前後のコードをマッピングするためのファイル。
- ソースマップを利用することで、デバッグ時にも、エラー箇所を(バンドル前の)オリジナルのソースコード によって参照できるようになるので、問題の特定がしやすくなる。
-
devtoolパラメーターで 'eval-source-map' を使用すると、オリジナルのソースを得ることができる。
7. ローダー
-
webpackを学ぶということは、ローダーを学ぶことである、と言い換えても良い。
-
ローダーはリソースをモジュールに変換する。
- ローダーを利用することで、JavaScript以外のコード(スタイルシート、画像ファイル)でもバンドルできるように、モジュール(JavaScriptで処理できる形式)に変換してくれる。
スタイルシートのバンドル - css-loader/style-loader
- css-loader
- スタイルシートを読み込むためのローダー。
- style-loader
- スタイルを
<style>
要素としてページに反映させるためのローダー。
- スタイルを
> npm install --save-dev webpack webpack-cli css-loader style-loader
ローダーの有効化
- 設定ファイルに対して
module - rules
パラメータを追加する。
// webpack.config.js
module.exports = {
...
modules: {
rules: [
{
test: /.\css$/, // ローダーを適用するファイル
use: [ // 適用するローダー
'style-loader', // 2. モジュールをページに組み込む
'css-loader' // 1. *.cssファイルがモジュール化
]
}
]
}
}
スタイルシートを要素で出力する - ExtractTextPlugin
- style-loader/css-loaderの組み合わせでは、バンドルされたスタイルは
<style>
要素としてページに埋め込まれる。- 一般的には、スタイルシートはページとは別ファイルとし、
<link>
要素で埋め込むのが普通。- ExtractTextPluginによって、バンドルしたリソースをファイルとして切り出す。
- 一般的には、スタイルシートはページとは別ファイルとし、
> npm install --save-dev webpack webpack-cli css-loader style-loader extract-text-webpack-plugin@next
// webpack.config.js
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
...
modules: {
rules: [
{
test: /.\css$/,
use: ExtractTextPlugin.extract({ use: 'css-loader' }) // 2. プラグインを割り当てる。
]
}
]
},
plugins: [
new ExtractTextPlugin('style.css'), // 1. スタイルシートの出力ファイル名を指定。
]
}
- プラグインを有効化するには、require関数でインポートした後、pluginsパラメーター配下でプロジェクトに登録する。
- 出力ファイル名には
[name].css
のようにプレイスホルダーを含めることもできる。 - ExtractTextPluginそのものはスタイルシートをモジュール化する機能は持たないので、extractメソッドの引数(useパラメーター)に、そのためのローダーを指定する。
その他のローダー
url-loader
-
画像をバンドルする。
- 無条件に全ての画像を束ねてしまうのは望ましくない。
- 理由1: オリジナルファイルよりもサイズが大きくなる
- 理由2: 解析のためのオーバーヘッドが発生する
- 無条件に全ての画像を束ねてしまうのは望ましくない。
-
リソースをファイルとして出力し、そのパスを管理する。
- url-loaderの
optins - limit
パラメーターで、画像サイズが閾値を超えたらファイル出力する、というような設定ができる。
- url-loaderの
-
本番環境で画像ファイルをCDNに配置する場合、
output - publicPath
パラメーターを設定する。- これによって、ビルド時にurl関数の値をリライトできる。
file-loader
- フォントファイルをバンドルする
- ネット経由でフォントを取得し利用する、Webフォントをバンドルする。
babel-loader
- ES2015以降のコードを、バンドル前にBabelによってトランスコンパイルする。
- ブラウザのサポート率が低いES2015以降の構文を、より低いバージョンのJavaScriptに変換する。
ts-loader
- TypeScriptのコードをコンパイルし、JavaScriptコード(既定ではES5のコード)に変換する。
sass-loader
- Sass(Syntactically Awesome Stylesheets。制御構文、関数、ミックスインなど、スタイルシートをよりコンパクトにまとめるための言語機能を備えた言語)をコンパイルし、標準的なCSSスタイルシートに変換するためのローダー。
- css-loader , style-loader , node-sass(Node.js上でSassをコンパイルするためのライブラリ)とのセットで利用する。
es-lint-loader
- ESLint(静的コード解析ツール)をwebpack上で実行するためのローダー。
- いろいろ設定があるらしい。