create-react-app が裏で何をやっているか理解する

# はじめに

`create-react-app` 便利ですね！
僕は `react` は `create-react-app` から入ったクチなので先輩方から「 `react` は昔は環境構築大変だった」とよく聞きますがいまいちピンと来ていません。
というわけで `create-react-app` が裏で何をやっているかまとめます。

## 対象読者

- `create-react-app` 使ってはいるけど、何やってるかあんまり分かってない人（僕）

# この記事でやること

1. `create-react-app` でアプリ作成
2. `create-react-app` でアプリ作成し、 `npm run eject` で `create-react-app` コマンドでパッケージされた状態を外す
3. 1と2を比較

以下、
1 の `create-react-app` のオリジナルの状態は `create-react-app-org` ディレクトリ、
2 の `npm run eject` は `create-react-app-eject` ディレクトリ、
でやることにします。


# 1. `create-react-app` を普通に実施

## 環境構築

`node` はインストールされてる前提。

```terminal
$ node -v 
v8.11.1
$ npm -v
5.6.0
$ npm install -g create-react-app
$ mkdir create-react-app-org
$ cd create-react-app-org
$ create-react-app hello-world
$ cd hello-world
```

## 実行

以下のどちらか。

development 環境

```terminal
$ npm start
```

production build 環境

```terminal
$ cd src
$ npm run build
$ cd ../build
$ npm install -g http-server
$ hs

ブラウザでアクセス http://127.0.0.1:8080
```

---

![SS 11.PNG](https://qiita-image-store.s3.amazonaws.com/0/35514/463e87df-117a-ed90-5205-596a10aab4bc.png)

---

はい、デフォルトのアプリ起動しました。
ほんと、簡単で素晴らしいですね。

ここで、 `create-react-app hello-world` 時のログ、

```terminal
$ create-react-app hello-world
Success! Created hello-world at /path/to/create-react-app-org/hello-world
Inside that directory, you can run several commands:

  npm start
    Starts the development server.

  npm run build
    Bundles the app into static files for production.

  npm test
    Starts the test runner.

  npm run eject
    Removes this tool and copies build dependencies, configuration files
    and scripts into the app directory. If you do this, you can’t go back!

We suggest that you begin by typing:

  cd hello-world
  npm start

Happy hacking!
$
```

が気になります。

# 2. `create-react-app` を `npm run eject`

## 環境構築

```terminal
$ mkdir create-react-app-eject
$ cd create-react-app-eject
$ create-react-app hello-world
$ cd hello-world
```

##  `npm run eject`

```terminal
$ npm run eject
> hello-world@0.1.0 eject /path/to/create-react-app-eject/hello-world
> react-scripts eject

? Are you sure you want to eject? This action is permanent. Yes
Ejecting...

Copying files into /path/to/create-react-app-eject/hello-world
  Adding /config/env.js to the project
  Adding /config/paths.js to the project
  Adding /config/polyfills.js to the project
  Adding /config/webpack.config.dev.js to the project
  Adding /config/webpack.config.prod.js to the project
  Adding /config/webpackDevServer.config.js to the project
  Adding /config/jest/cssTransform.js to the project
  Adding /config/jest/fileTransform.js to the project
  Adding /scripts/build.js to the project
  Adding /scripts/start.js to the project
  Adding /scripts/test.js to the project

Updating the dependencies
  Removing react-scripts from dependencies
  Adding autoprefixer to dependencies
  Adding babel-core to dependencies
  Adding babel-eslint to dependencies
  Adding babel-jest to dependencies
  Adding babel-loader to dependencies
  Adding babel-preset-react-app to dependencies
  Adding babel-runtime to dependencies
  Adding case-sensitive-paths-webpack-plugin to dependencies
  Adding chalk to dependencies
  Adding css-loader to dependencies
  Adding dotenv to dependencies
  Adding dotenv-expand to dependencies
  Adding eslint to dependencies
  Adding eslint-config-react-app to dependencies
  Adding eslint-loader to dependencies
  Adding eslint-plugin-flowtype to dependencies
  Adding eslint-plugin-import to dependencies
  Adding eslint-plugin-jsx-a11y to dependencies
  Adding eslint-plugin-react to dependencies
  Adding extract-text-webpack-plugin to dependencies
  Adding file-loader to dependencies
  Adding fs-extra to dependencies
  Adding html-webpack-plugin to dependencies
  Adding jest to dependencies
  Adding object-assign to dependencies
  Adding postcss-flexbugs-fixes to dependencies
  Adding postcss-loader to dependencies
  Adding promise to dependencies
  Adding raf to dependencies
  Adding react-dev-utils to dependencies
  Adding resolve to dependencies
  Adding style-loader to dependencies
  Adding sw-precache-webpack-plugin to dependencies
  Adding url-loader to dependencies
  Adding webpack to dependencies
  Adding webpack-dev-server to dependencies
  Adding webpack-manifest-plugin to dependencies
  Adding whatwg-fetch to dependencies

Updating the scripts
  Replacing "react-scripts start" with "node scripts/start.js"
  Replacing "react-scripts build" with "node scripts/build.js"
  Replacing "react-scripts test" with "node scripts/test.js"

Configuring package.json
  Adding Jest configuration
  Adding Babel preset
  Adding ESLint configuration

Running npm install...
added 1 package and updated 2 packages in 8.329s
Ejected successfully!

Please consider sharing why you ejected in this survey:
  http://goo.gl/forms/Bi6CZjk1EqsdelXk1

$ 
```

## 何が起きたのか？

`create-react-app` コマンドで作られた環境は `react-scripts` でまるっとまとめられていたのですが、それを解放して、必要なモジュールを一つ一つ手でインストールしたのと同じ状態になりました。

## `npm run eject` 後の状態でも

development 環境

```terminal
$ npm start
```

production build 環境

```terminal
$ cd src
$ npm run build
$ cd ../build
$ npm install -g http-server
$ hs

ブラウザでアクセス http://127.0.0.1:8080
```

`create-react-app` と同様、どちらでもアプリ実行可能です。

# 3. 比較

`npm run eject` 前後の比較をします。

## package.json

### `create-react-app` 素の状態

```terminal
/path/to/create-react-app-org/hello-world$ cat package.json 
```

```json
{
  "name": "hello-world",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^16.3.2",
    "react-dom": "^16.3.2",
    "react-scripts": "1.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
}
```

### `npm run eject` 後

```terminal
/path/to/create-react-app-eject/hello-world$ cat package.json 
```

```json
{
  "name": "hello-world",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "autoprefixer": "7.1.6",
    "babel-core": "6.26.0",
    "babel-eslint": "7.2.3",
    "babel-jest": "20.0.3",
    "babel-loader": "7.1.2",
    "babel-preset-react-app": "^3.1.1",
    "babel-runtime": "6.26.0",
    "case-sensitive-paths-webpack-plugin": "2.1.1",
    "chalk": "1.1.3",
    "css-loader": "0.28.7",
    "dotenv": "4.0.0",
    "dotenv-expand": "4.2.0",
    "eslint": "4.10.0",
    "eslint-config-react-app": "^2.1.0",
    "eslint-loader": "1.9.0",
    "eslint-plugin-flowtype": "2.39.1",
    "eslint-plugin-import": "2.8.0",
    "eslint-plugin-jsx-a11y": "5.1.1",
    "eslint-plugin-react": "7.4.0",
    "extract-text-webpack-plugin": "3.0.2",
    "file-loader": "1.1.5",
    "fs-extra": "3.0.1",
    "html-webpack-plugin": "2.29.0",
    "jest": "20.0.4",
    "object-assign": "4.1.1",
    "postcss-flexbugs-fixes": "3.2.0",
    "postcss-loader": "2.0.8",
    "promise": "8.0.1",
    "raf": "3.4.0",
    "react": "^16.3.2",
    "react-dev-utils": "^5.0.1",
    "react-dom": "^16.3.2",
    "resolve": "1.6.0",
    "style-loader": "0.19.0",
    "sw-precache-webpack-plugin": "0.11.4",
    "url-loader": "0.6.2",
    "webpack": "3.8.1",
    "webpack-dev-server": "2.9.4",
    "webpack-manifest-plugin": "1.3.2",
    "whatwg-fetch": "2.0.3"
  },
//略
```

`create-react-app` 素の状態では、dependencies が react, react-dom, react-scripts の僅か3パッケージだったのに対し、 `npm run eject` するとパッケージ数が途端に増えました。


## ディレクトリ構成

### `create-react-app` 素の状態

```terminal
/path/to/create-react-app-org/hello-world$ tree -I 'node_modules'
.
├── README.md
├── package-lock.json
├── package.json
├── public
│   ├── favicon.ico
│   ├── index.html
│   └── manifest.json
└── src
    ├── App.css
    ├── App.js
    ├── App.test.js
    ├── index.css
    ├── index.js
    ├── logo.svg
    └── registerServiceWorker.js

2 directories, 13 files
$
```

### `npm run eject` 後

```terminal
/path/to/create-react-app-eject/hello-world$ tree -I 'node_modules'
.
├── README.md
├── config
│   ├── env.js
│   ├── jest
│   │   ├── cssTransform.js
│   │   └── fileTransform.js
│   ├── paths.js
│   ├── polyfills.js
│   ├── webpack.config.dev.js
│   ├── webpack.config.prod.js
│   └── webpackDevServer.config.js
├── package-lock.json
├── package.json
├── public
│   ├── favicon.ico
│   ├── index.html
│   └── manifest.json
├── scripts
│   ├── build.js
│   ├── start.js
│   └── test.js
└── src
    ├── App.css
    ├── App.js
    ├── App.test.js
    ├── index.css
    ├── index.js
    ├── logo.svg
    └── registerServiceWorker.js

5 directories, 24 files
$
```

`create-react-app` 素の状態では、ほぼソースファイルのみだったのに対し、 `npm run eject` すると隠れていた config ファイルが現れます。


# まとめ

本来大量のパッケージと設定ファイルをしこしこ用意して `react` 環境を作らねばならなかったところ、
`create-react-app` コマンドは一発でその環境を用意し、良い感じに隠蔽し、ソースファイルを見やすく、開発しやすくするツールでした。
＃もちろん、細かく手を入れたければ `npm run eject` してカスタマイズ。もしくはイチから環境構築。

# 参考

[Facebook公式のcreate-react-appコマンドを使ってReact.jsアプリを爆速で作成する](https://qiita.com/chibicode/items/8533dd72f1ebaeb4b614)
[Facebook 公式ツール Create React App を使って React 開発の初歩を学ぶチュートリアル](https://qiita.com/kent_ocean/items/d429804b5ad0d7570ece)
[2018 年 React と Redux のエコシステム総まとめ](https://qiita.com/kotarella1110/items/e76bf9cca665157f3d2e)
[ゼロから始めるJavaScript生活](https://qiita.com/takahashim/items/7838334d1451fb0a9811)