reactjsでアプリケーション構築した際、
production環境へのデプロイを見据えてwebpack周りの設定を見直したので
それをまとめました。
webpack.config.jsのリネーム
webpack.configでbabelなコードが書きたかったのでリネーム
あわせて、constじゃなくて、importでライブラリを読み込むように変更
リネーム & 書き換え
% mv webpack.config.js webpack.config.babel.js
// before
const webpack = require('webpack');
// after
import webpack from 'webpack';
webpack-merge
webpack.configをマージするためのモジュール。
共通設定ファイルと環境別の設定ファイルをマージするみたいな用途で利用する。
環境別でwebpack.configをかき分けたい。
手動でオブジェクトのdeep mergeするのはいやだ!みたいな人は使おう。
インストール
npm i -D webpack-merge
設定
- 共通設定: 
webpack.config.babel.js - 
NODE_ENV=production:webpack.config.prod.babel.js - 
NODE_ENV=development:webpack.config.dev.babel.js 
import merge from 'webpack-merge';
// NODE_ENVで分岐
const config = process.env.NODE_ENV === 'production' ?
    require('./webpack.config.prod.babel.js') :
    require('./webpack.config.dev.babel.js');
const common = {
  context: __dirname,
  entry: './src/index.jsx',
  output: {
    path: `${__dirname}/public`,
    publicPath: '/',
    filename: 'bundle.js',
  },
  module: {
    preLoaders: [
      { test: /\.jsx?$/, exclude: /node_modules/, loader: 'eslint' },
    ],
    loaders: [
      { test: /\.jsx?$/, exclude: /node_modules/, loaders: ['react-hot-loader/webpack', 'babel'] },
      { test: /\.css$/, loaders: ['style', 'css'] },
      { test: /\.json$/, loader: 'json' },
      { test: /\.(jpe?g|png|gif)$/, loader: 'url?limit=10000' },
    ],
  },
  resolve: {
    extensions: ['', '.js', '.jsx', '.json'],
  },
  eslint: {
    configFile: './.eslintrc',
  },
};
module.exports = merge(common, config);
npm runコマンドでwebpack呼ぶときは適宜NODE_ENVをつける。
  scripts: {
    start: webpack-dev-server --history-api-fallback --hot --inline --progress --colors,
    build-debug: webpack --display-error-details --progress --colors,
    build: NODE_ENV=production webpack --progress --colors,
  },
UglifyJsPlugin などでjsを最適化・圧縮
UglifyJsPluginで圧縮して、DedupePlugin, AggressiveMergingPluginで最適化する。
詳細が知りたければこちら
list of plugins
設定
import webpack from 'webpack';
module.exports = {
  debug: false,
  devtool: false,
  plugins: [
    new webpack.optimize.DedupePlugin(),
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false,
      },
      comments: false,
    }),
    new webpack.optimize.AggressiveMergingPlugin(),
  ],
};
html-webpack-plugin
index.htmlに直接、bundle.jsを書き込んでいたところに、
ハッシュコード付きのscriptタグを挿入したかったのでこのプラグインを導入した。
ハッシュコードってのはこういうやつね
<script src="/bundle.js?4398c4ce46af21776b47"></script>
インストール
npm i -D html-webpack-plugin
テンプレートファイルの用意
index.htmlをejsファイルにリネームし、<script src="/bundle.js" />なコードを除去する。
mv src/index.html src/index.template.ejs
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <title>title</title>
  </head>
  <body>
    <div id="root"></div>
-  <script type="text/javascript" src="/bundle.js"></script></body>
  </body>
</html>
設定
pluginsに下記のように設定すると、npm run buildした時にいい感じでindex.htmlを吐いてくれる。
他にもscriptとかmetaタグを<head>に入れたくなったらマニュアル見てね。
import HtmlWebpackPlugin from 'html-webpack-plugin';
  plugins: [
    new HtmlWebpackPlugin({
      hash: true,
      filename: 'index.html',
      favicon: './src/favicon.jpg',
      template: './src/index.template.ejs',
      inject: 'body',
    }),
  ],
出力サンプル
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <title>title</title>
  <link rel="shortcut icon" href="/favicon.jpg"></head>
  <body>
    <div id="root"></div>
  <script type="text/javascript" src="/bundle.js?4398c4ce46af21776b47"></script></body>
</html>
webpack-manifest-plugin
bundle.jsって名前だとCDNに上げた時、世代管理できない。
自動生成されたファイル名にしたいってなったらこれ。
インストール
npm i -D webpack-manifest-plugin chunk-manifest-webpack-plugin
設定
下記設定だと、entryのキーが出力後のファイル名になるので注意。
hot loadしたいのでproduction環境以外は[chunkhash]つけない。
import ManifestPlugin from 'webpack-manifest-plugin';
import ChunkManifestPlugin from 'chunk-manifest-webpack-plugin';
module.exports = {
  context: __dirname,
  entry: {
+    bundle: './src/index.jsx',
  },
   output: {
     path: `${__dirname}/public`,
     publicPath: '/',
-    filename: 'bundle.js',
+    filename: '[name].js',
   },
  plugins: [
+    new ManifestPlugin(),
+    new ChunkManifestPlugin({
+      filename: 'chunk-manifest.json',
+      manifestVariable: 'webpackManifest',
+    }),
  ]
module.exports = {
  context: __dirname,
  entry: {
+    bundle: './src/index.jsx',
  },
   output: {
     path: `${__dirname}/public`,
     publicPath: '/',
-    filename: 'bundle.js',
+    filename: '[name].[chunkhash].js',
   },
  ]
npm run buildしたらこういうのが出力される。
manifest.jsonも吐き出されるので、なにかと連携したければそれを読み込むといいかも。
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <title>title</title>
  </head>
  <body>
    <div id="root"></div>
  <script type="text/javascript" src="/bundle.b00a9f3a29b791781cc5.js?7490b7329f7ad1443141"></script></body>
</html>
copy-webpack-plugin
本番環境にデプロイして、いざ動作確認してみたら、
react-router使ってるせいで、404出ちゃったとかありませんか。
そしたら、apacheの設定ファイル置きたくなったり、sass用のリダイレクトファイルを
public配下に設置したくなりますよね。 そいういう時にこれ使いました。
例 .htaccess, _redirects
import CopyWebpackPlugin from 'copy-webpack-plugin';
// 〜略〜
  plugins: [
    new CopyWebpackPlugin([
      { from: { glob: './src/static/**', dot: true }, to: '[name].[ext]' },
    ]),
Google Analytics
react-router使ってたら、画面遷移時にアナリティクスに取れなくなるのでこれを導入
インストール方法
npm i -S react-ga
設定
import React from 'react';
import ReactDOM from 'react-dom';
import ReactGA from 'react-ga';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import App from '../containers/App';
window.React = React;
ReactGA.initialize('UA-123456-7');
const logPageView = () => {
  ReactGA.set({ page: window.location.pathname });
  ReactGA.pageview(window.location.pathname);
};
ReactDOM.render((
  <Router history={browserHistory} onUpdate={logPageView}>
    <Route path="/">
      <IndexRoute component={App} />
      <Route path="/:userName" component={App} />
    </Route>
  </Router>
), document.getElementById('root'));
所管
あとやり残してることあるかな。気が付いたら追記する。