webpack-serveとreact-routerでSPA環境(Deprecated)

追記: webpack-serve は非推奨となり webpack-dev-server が開発再開しました。新しく始める際はスタックに webpack-serve を採用しないほうがよいでしょう。

実は react あんまり関係ない。webpack-serve でSPAホストする際にフォールバックコンテンツの設定方法でちょっとハマったのでメモ。

実行環境は [React.js 開発環境を webpack4 + webpack-serve + babel 7 で作る](https://qiita.com/ma2saka/items/5b88387c7decebe9e925) の流れで作ったものをそのまま利用しています。したがって babel7, webpack4 です。

## webpack-serve と react-router

react-router-dom をインストールします

```
npm install --save react-router-dom
```

## src/index.js をルーティング対応にする

ここではルートとして `/` と `/welcome` を持つとても簡単なアプリケーションを作成しました。

```jsx:index.js
import React from "react";
import { render } from "react-dom";
import { BrowserRouter as Router, Route,Switch, Link } from "react-router-dom";
import "./index.css";

const Root = () => (
  <div>
    <p>Roooooooooot component</p>
  </div>
);

const Welcome = () => (
  <div>
    <p>Welcome component</p>
  </div>
);

render(
  <Router>
    <div>
      <h1>React Router</h1>
      <div>
        <ul>
          <li>
            <Link to="/welcome">Welcome</Link>
          </li>
          <li>
            <Link to="/">App Root</Link>
          </li>
        </ul>
      </div>
      <Switch>
        <Route exact path="/welcome" component={Welcome} />
        <Route path="/" component={Root} />
      </Switch>
    </div>
  </Router>,

  document.getElementById("content")
);

```

## フォールバックコンテンツを返す設定をする

connect-history-api-fallback をインストールします。

```
npm install --save connect-history-api-fallback koa-connect
```

ミドルウェアを有効にするため、webpack-serve のアドオン設定関数を追加します。ここでは `webpack.config.js` に書いてますが、設定コールバック書き始めるあたりから `serve.config.js` に分離したほうが見通しいい気がします。

output として `publicPath: '/',`を設定し忘れると、`/no/file/text.html` のように、`/` を含んだパスのときにフォールバックコンテンツを返してくれないために注意が必要です。

```webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

const convert = require("koa-connect");
const historyApiFallback = require("connect-history-api-fallback");

// アドオンに historyApiFallback を追加
const addon = (app, middleware, option) => {
  app.use(convert(historyApiFallback()));
};

module.exports = {
    mode: process.env.WEBPACK_SERVE ? 'development' : 'production',
    entry: {
      main: path.resolve(__dirname, './src/', "index.js")
    },
    output: {
        path: path.resolve(__dirname, './dist/'),
        publicPath: '/',
        filename: '[name]-[hash].js'
    },
    module: {
      rules: [
      {
        test: /\.jsx?$/,
        exclude:[ /node_modules/ ],
        loader: 'babel-loader'
      },
      {
        test: /\.css$/,
        exclude: [/node_modules/],
        use: ["style-loader", { loader: "css-loader", options: { modules: true } }]
      }]
    },
    devtool: 'source-map',
    serve: {
        open: true,
        port: 8080,
        content: path.resolve(__dirname, 'public'),
        add: addon
    },
    plugins: [new HtmlWebpackPlugin({ template: "src/index.html" })]
}
```

以上、おわり。

これで開発サーバ起動すると、`/welcome` に対しても `index.html` のコンテンツが返却されるようになりました。

## connect-history-api-fallback

「404ページに対してフォールバックコンテンツを設定する」ミドルウェアではなく「History APIを利用して作成されるSPAのためのindexページをプロキシする」ミドルウェアなのでこういう名前なのはわかりにくいのか、わかりやすいのか・・・?

- https://github.com/bripkens/connect-history-api-fallback

>Middleware to proxy requests through a specified index page, useful for Single Page Applications that utilise the HTML5 History API.

最初、こいつが history APIをクライアント側で処理するライブラリかとばかり思ってスルーしていたのが冒頭に書いた「ちょっとハマった」の実態です。

## koa-connect

`connect` のミドルウェアを koa 向けに変換するアダプタ。名前もうちょっとわかりやすくしてもいいんじゃないかとは思う。 https://github.com/vkurchatkin/koa-connect リポジトリ見ると最終更新2年前。まあミドルウェアのインターフェイスなんてそうそう変更されないし別にいいのか。