create-react-app v2.xで作成したプロジェクトにおいてWeb Workerを使用すると `Uncaught ReferenceError: window is not defined` が発生する

## 現象

`create-react-app` の version 2.0 以降で生成したプロジェクトにおいて、worker-loaderを使用してWeb Workerのスクリプトを読み込み実行すると、`Uncaught ReferenceError: window is not defined (bootstrap:1)` という例外が発生してWeb Workerの処理が実行されない。

## 原因

`react-scripts v2.1.1` の `webpack (v4.19.1)` 設定に問題があり、Web Workerが適切に処理できていない (Workerのコンテキストが解釈できていない) 模様。
該当すると思われるissueは以下。

- [webwork can't find window on 'bootstrap' · Issue #6629 · webpack/webpack](https://github.com/webpack/webpack/issues/6629)
- [Webpack 4.0.1 | WebWorker `window is not defined` · Issue #6642 · webpack/webpack](https://github.com/webpack/webpack/issues/6642)
- [Uncaught ReferenceError: window is not defined · Issue #166 · webpack-contrib/worker-loader](https://github.com/webpack-contrib/worker-loader/issues/166)

`worker-loader` が適切に処理してくれると嬉しいのですが...

## 対応

通常、Workerスクリプト内では `onmessage` イベントや `postMessage` メソッドはグローバルなスコープに定義されている ([DedicatedWorkerGlobalScope - Web API インターフェイス | MDN](https://developer.mozilla.org/ja/docs/Web/API/DedicatedWorkerGlobalScope)) ので直接呼び出すことができますが、ブラウザの `window` オブジェクトにはこれらのイベント・メソッドは存在しないため、何も知らない WebPack で適切に処理できません。

上記の `DedicatedWorkerGlobalScope` で定義されている内容を WebPack の理解するグローバルコンテキストに追加するよう設定してやればよいのですが、それは面倒なので、Woekerスクリプト内ではすべて `self` オブジェクト経由でアクセスし、WebPack にはグローバルなオブジェクトに `self` っていうのがあるよ、という設定をしてやります。

### 1. Workerのイベントハンドラや各種プロパティに `self` オブジェクトを経由してアクセスする

イメージとしては以下のような感じです。（ゴニョゴニョっていうのがWeb Workerでやりたい処理）

```js:before/worker.js
onmessage = (evt) => {
  const { params } = evt.data;
  // ゴニョゴニョ
  postMessage({ payload: newData });
};
```

```js:after/worker.js
self.onmessage = (evt) => {
  const { params } = evt.data;
  // ゴニョゴニョ
  self.postMessage({ payload: newData });
};
```


### 2. WebPackの設定を変更し、グローバルオブジェクトに `self` を追加する

`create-react-app` で作成したプロジェクトでは WebPack の設定などは `react-scripts` に隠蔽されています。
特に問題なければビルド環境の複雑さから逃れられるので良いのですが、今回のような問題があったときには困ってしまいます。

`npm run eject` コマンドで `react-scripts` に隠蔽されている各種設定が触れるようになります。

```sh
> npm run eject
```

`package.json` の内容が更新され、`npm install` が再実行されます。
プロジェクトフォルダのルートに `config` と `scripts` というフォルダが作成され、そこに `react-scripts` と各種設定ファイルが展開されます。

`config` フォルダにwebpackの設定が格納されています。
`output.globalObject` に `'self'` を追加します。

開発用と本番用の2つありますので、両方設定を変更してください。

```js:config/webpack.config.dev.js
module.exports = {
  // ...省略
  output: {
    // Add /* filename */ comments to generated require()s in the output.
    pathinfo: true,
    // This does not produce a real file. It's just the virtual path that is
    // served by WebpackDevServer in development. This is the JS bundle
    // containing code from all our entry points, and the Webpack runtime.
    filename: 'static/js/bundle.js',
    // There are also additional JS chunk files if you use code splitting.
    chunkFilename: 'static/js/[name].chunk.js',
    // This is the URL that app is served from. We use "/" in development.
    publicPath: publicPath,
    // Point sourcemap entries to original disk location (format as URL on Windows)
    devtoolModuleFilenameTemplate: info =>
      path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'),
    // ***********************
    // Web Worker用に以下を追加
    // ***********************
    globalObject: 'self',
  },
```

```js:config/webpack.config.prod.js
module.exports = {
  // ...省略
  output: {
    // The build folder.
    path: paths.appBuild,
    // Generated JS file names (with nested folders).
    // There will be one main bundle, and one file per asynchronous chunk.
    // We don't currently advertise code splitting but Webpack supports it.
    filename: 'static/js/[name].[chunkhash:8].js',
    chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js',
    // We inferred the "public path" (such as / or /my-project) from homepage.
    publicPath: publicPath,
    // Point sourcemap entries to original disk location (format as URL on Windows)
    devtoolModuleFilenameTemplate: info =>
      path
        .relative(paths.appSrc, info.absoluteResourcePath)
        .replace(/\\/g, '/'),
    // ***********************
    // Web Worker用に以下を追加
    // ***********************
    globalObject: 'self',
  },
```

設定を変更したら `npm start` して例外が発生せずに Web Worker が呼び出されることを確認します。

---

## そもそも Web Worker って何？

> Web Workers は、Web コンテンツがスクリプトをバックグラウンドのスレッドで実行するためのシンプルな手段です。Worker スレッドは、ユーザインターフェイスを妨げることなくタスクを実行できます。
>
> *Web Worker を使用する - Web API インターフェイス | MDN*
> https://developer.mozilla.org/ja/docs/Web/API/Web_Workers_API/Using_web_workers

Webアプリで巨大なファイルを読み込むなどの重たい処理を行う場合に **Web Worker** を使うと描画に影響を与えずに処理ができます。