React 入門 - webpack 導入編

React に入門するために webpack で一から開発環境を作ってみた話です

## この記事を読むとわかること

* React の導入前に最低限必要な周辺ツールの概要
* 周辺ツールを入れるための webpack.config などの記述方法
* ~~フロントエンドの技術を把握する大変さ~~

## 動機

* React プロジェクトの webpack を最新バージョンに上げたい
* しかし webpack.config の記述方法が大幅に変わっている
* さらに人の書いたコードを流用していたため解読できない箇所が多々ある
* それなら習作を兼ねて一から再入門してみよう！

今回は理解を深めることが目的なので [create-react-app](https://github.com/facebookincubator/create-react-app) のようなツールは使用しません 

## 最低限必要なツールとは

以下のような要件が満たされる程度を想定しています :

1. ビルドできる
    * npm モジュールを利用できる
    * コードの依存関係を解決できる
2. スタイルシートや画像なども含めてビルドできる
    * SCSS でスタイルシートを記述できる
    * 小さな画像は data URI スキームで .js ファイルに埋め込める
3. 開発中はファイル変更時に画面を自動でリロードできる
    * 確認するブラウザが複数個になっても全て自動でリロードできる
    * 開発ビルドと本番ビルドで設定を切り替えられる
4. ビルド後のコードを適切な単位で分割できる
    * 巨大な .js ファイルが生成されるのを防げる
    * 必要なファイルを必要なタイミングで取得できる
5. ビルド後のコードを圧縮できる
    * 生成される .js ファイルを難読化できる
    * 分割されたファイルから重複を取り除ける

一つ白状すると、ここまでは React とは何の関係もありません :innocent: 

## 作業リポジトリ

これらの要件を満たすための実装内容と、本来の趣旨である React の導入方法については、[SPA Starter](https://github.com/x7c1/spa-starter/) リポジトリの [Pull Requests](https://github.com/x7c1/spa-starter/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aclosed+created%3A%3C2017-10-29) から確認できます

* [#1. ビルドの準備](https://github.com/x7c1/spa-starter/pull/1)
* [#2. スタイルシートと画像の追加](https://github.com/x7c1/spa-starter/pull/2)
* [#3. 開発サーバの導入](https://github.com/x7c1/spa-starter/pull/3)
* [#4. バンドルの分割](https://github.com/x7c1/spa-starter/pull/4)
* [#5. 出力結果の圧縮](https://github.com/x7c1/spa-starter/pull/5)
* [#6. React の導入](https://github.com/x7c1/spa-starter/pull/6)

それぞれのプルリクで具体的に何が実装されているのか、順番に追ってみましょう

## ビルドの準備

[#1](https://github.com/x7c1/spa-starter/pull/1) はビルドできるだけの最小構成です。
最小ではありますが、すでに以下のようないくつかの大切な機能を確認することができます :

* 外部ライブラリ `lodash` への依存を解決できること
* ビルドの出力先 `./dist` を `output.path` によって設定できること
* `HtmlWebpackPlugin` によってテンプレート `index.html` を外に切り出せること
* そして `npm run build` でビルドできること

## スタイルシートと画像の追加

[#2](https://github.com/x7c1/spa-starter/pull/2) では以下の機能が追加されています :

* [css-loader](https://github.com/webpack-contrib/css-loader) による CSS の読み込み
* [sass-loader](https://github.com/webpack-contrib/sass-loader) による SCSS の読み込み
* [file-loader](https://github.com/webpack-contrib/file-loader) による画像ファイルの読み込み
* [url-loader](https://github.com/webpack-contrib/url-loader) による画像の `data:` スキーム埋め込み
* [extract-text-webpack-plugin](https://github.com/webpack-contrib/extract-text-webpack-plugin) による .css ファイルの切り出し
* [postcss-loader](https://github.com/postcss/postcss-loader) と [cssnano](http://cssnano.co/) による .css ファイルの最適化・圧縮


スタイルを適用できるようになって、少しだけアプリケーションらしい形になってきました。SCSS を扱えるようになったのも現代的な感じがします。ただ、この時点ではまだ毎回ビルドしなければ確認できないので、残念ながら実用性はありません。

## 開発サーバの導入


[#3](https://github.com/x7c1/spa-starter/pull/3) には以下のような内容が含まれています :

* [express](https://github.com/expressjs/express) による開発用サーバの追加
* [webpack-hot-middleware](https://github.com/glenjamin/webpack-hot-middleware) による自動リロード (hot reloading) の設定
* `npm run` 時に環境変数を切り替えるために [better-npm-run](https://github.com/benoror/better-npm-run) を追加
* [webpack-merge](https://github.com/survivejs/webpack-merge) によって開発用と本番用の webpack.(dev|prod) を分割
* [clean-webpack-plugin](https://github.com/johnagan/clean-webpack-plugin) によってビルド前に出力先を初期化
* デバッグログ用の [debug](https://github.com/visionmedia/debug) モジュール追加

ようやく自動リロードが実現されました。ファイルを変更するたびに独りでに画面が更新されるのはとても快適です。ビルド結果の最適化を除けば、もうなかなかの実用性があると言えるでしょう。

同時に、このあたりから webpack の設定ファイルが邪悪の片鱗を見せてくるようになります。設定が腐っていくのを防ぐためにも、適宜整理していきたいところです。

## バンドルの分割

[#4](https://github.com/x7c1/spa-starter/pull/4) ではファイルを分割して出力するための設定を加えています :

* [webpack.optimize.CommonsChunkPlugin](https://webpack.js.org/plugins/commons-chunk-plugin/) によるファイル分割の設定
  * `./node_modules` 配下のコードを `vendor.[hash].js` としてまとめる設定
  * webpack のブートストラップ処理を `manifest.[hash].js` としてまとめる設定
* [dynamic import](https://webpack.js.org/api/module-methods/#import-) によってファイルを遅延読み込みさせるサンプルコードの追加

`import` の単位を工夫することで「最初に開くページではスクリプトの読み込みを最小限にする」といった最適化を加えられるようになりました。また、たとえば滅多に更新されないライブラリと頻繁に変わるドメインロジックのファイルを分ければ、ブラウザキャッシュの効率化を図ることもできます。

ちなみに、この分割によって生じたそれぞれのファイル群はチャンク (chunk) とも呼ばれています。

## 出力結果の圧縮

[#5](https://github.com/x7c1/spa-starter/pull/5) に含まれる内容 :

* [uglifyjs-webpack-plugin](https://www.npmjs.com/package/uglifyjs-webpack-plugin) による圧縮の設定を追加
* チャンクをさらに圧縮するための [AggressiveMergingPlugin](https://github.com/webpack/docs/wiki/list-of-plugins#aggressivemergingplugin) の追加

これらの設定を適用すると人にはほとんど読めないコードが出力されるようになります。そのため、もしもビルドによってどのようなコードが出力されているのか目視で確認したくなった場合、この設定を解除する必要があります。

## React の導入

[#6](https://github.com/x7c1/spa-starter/pull/6) でついに念願の React 導入に辿り着きました :

* ブラウザで React を使うために [react](https://www.npmjs.com/package/react) と [react-dom](https://www.npmjs.com/package/react-dom) モジュールを追加
* コンポーネントを hot reloading させるために `module.hot.accept` の記述を追加
* JSX 記法をパースするために [babel-plugin-transform-react-jsx](https://babeljs.io/docs/plugins/transform-react-jsx/) の設定を追加
* dynamic import を babel 経由でも利用できるように [babel-preset-stage-0](https://babeljs.io/docs/plugins/preset-stage-0/) の設定を追加

興味深いことに、今回は babel を使っていながら ES5 への変換は省いています。
2017-12 現在では (対象のブラウザを限定できるなら) ES6 のままで問題なく動かせるためです。


## 終わり

おつかれさまでした :tada: 

これで [SPA Starter](https://github.com/x7c1/spa-starter/) を `git clone` してから `npm install` するだけで、すぐに React による開発を始められるようになりました。もう複雑怪奇な `webpack.config` や各種ツールの導入について悩む必要はありません。`create-react-app` のようなブラックボックスに頼る必要もありません。暗号めいた謎の設定も残っていないので、今後のバージョンアップで立ち往生することもないでしょう。

以上。快適な React 開発を！