React + TypeScript + webpackで作るWebアプリケーション

# 概要
以下の技術を用いた簡単な静的Webアプリケーションを作る環境を学習がてら構築したので備忘録として残します。

| 使用技術 |
|:--|
| React |
| TypeScript |
| Sass |
| webpack |

## リポジトリ
```
https://github.com/enuswi/react-app
```
### 本投稿時点のcommit
```
https://github.com/enuswi/react-app/commit/a74409890292c4e96140361d682a4ec869510a89
```
＊cloud9(EC2)環境上であれば上記リポジトリのREADME「開発セットアップ(Cloud9)」の通りで動作確認できると思います(2020.01.06)

# 構成

```
・
├── dist ...ビルドされたファイルが出力されるフォルダ。
│   ├── index.html
│   ├── main.bundle.js
│   └── style.css
├── node_modules
├── src ...実際に作業するフォルダ。
│   ├── index.html
│   ├── main.tsx
│   └── style.scss
├── package-lock.json
├── package.json
├── tsconfig.json ... typescriptの設定ファイル
└── webpack.config.js ...webpackの設定ファイル
```

## srcフォルダ
実際に作業するフォルダ。
ここのファイルを編集していきます。

## distフォルダ
ビルドされたファイルが出力されるフォルダ。
ここに出力されたファイルをS3におけば簡単に公開もできます。

## tsconfig.json
TypeScriptファイルをコンパイルするのに必要な設定を指定します。
今回は使用していませんが、filesプロパティやincludeプロパティを用いることでコンパイルするファイルを指定することもできるみたいです。

```tsconfig.json
{
    "compilerOptions": {
        "sourceMap": true,
        "target": "es5",        // TSはECMAScript 5に変換
        "module": "es2015",     // TSのモジュールはES Modulesとして出力
        "jsx": "react",         // JSXの書式を有効に設定
        "moduleResolution": "node",
        "lib": [
            "es2019",
            "dom"
        ]
    }
}
```

### compilerOptions
...その名の通り、コンパイルする際のオプション設定です。
各オプションにはデフォルトが設定されている為、省略も可能です。
以下、使用オプションのみ簡単に説明します。

### sourceMap
boolean型 (default: false)
...対応する.mapファイルを生成します。

↓今回で言えば以下の表の形で生成することができます。

| コンパイルしたファイル(.js .css) | ソースマップファイル(.map) |
|:--|:-- |
| main.bundle.js |　main.bundle.js.map |
| style.css | style.css.mapp |

#### ソースマップファイルを一緒に配置すると
ブラウザが元のソースを再構築し、それを開発者ツールから確認できるようになります。圧縮されたファイルやトランスパイルされた今回のTypeScriptのファイルのようなファイルをデバッグするのには役立ちそうです。

＊あくまで開発時に使うもの(の認識)なので、ファイルの取り扱いには注意が必要かなと思いました。
＊画像はChromeの開発者ツール表示

1. ソースマップファイルなし
<img width="1431" alt="screenshot 2020-01-05 18.35.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46053/36650f2c-30bf-09b0-3745-44a9cf2d6381.png">
2. ソースマップファイルあり
<img width="1431" alt="screenshot 2020-01-05 19.06.19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46053/2587e33a-c4c1-d754-418d-e0f5ce68c0ea.png">

### target
string型 (default: ES3)
...対象とするECMAScriptのバージョンを指定します。

### module
string型　(default: targetプロパティの値により変化)
...モジュールの方式を指定します。

### jsx
string型 (default: Preserve)
...(.tsx)ファイルのJSXをサポートします。
モードはPreserveかreactの2種類あり今回はreactを選択します。

ReactにおけるJSXはReact.createElement()の糖衣構文です。
jsxの設定をすることでReact.createElement()の形にコンパイルしてくれます。

```main.tsx
class Game extends React.Component {...}

// ↓これを
ReactDOM.render(
    React.createElement(
        Game, {}, null
    ),
    document.getElementById('root')
);

//↓このように書き換えることが出来ます。ちょっとスッキリ。
ReactDOM.render(
    <Game />,
    document.getElementById('root')
);
```

### moduleResolution
string型　(default: Classic)
... モジュールをどのように解決するのかを決定します。
モードはnodeとClassicの2種類があり今回はnodeを選択しています。
nodeモードではnode.jsのモジュール解決アルゴリズムを真似たアルゴリズムが用いられます。
＊細かいアルゴリズムについては今度調べてみようと思います。

### lib
string[]型　(default: targetプロパティの値により変化)
...コンパイルに含めるライブラリのファイルの一覧を指定します。

＊各オプションについては以下で詳しくまとめられております
http://js.studio-kingdom.com/typescript/project_configuration/compiler_options

## webpack.config.js
webpackを用いてビルドを行うのでその為の設定を行います。

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

module.exports = {
    //mode: "development",
    mode: "production",
    devtool: "source-map",

    entry: {
        main: "./src/main.tsx",
    },
    output: {
        path: path.join(__dirname, "/dist"),
        filename: "[name].bundle.js"
    },
    module: {
        rules: [
            {
                test: /\.tsx?$/,
                use: "ts-loader"
            },
            {
                test: /\.scss$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'sass-loader'
                ]
            }
        ]
    },
    resolve: {
        extensions: [".ts", ".tsx", ".js", ".json"]
    },
    plugins: [
        // index.htmlファイルをdist以下に配置
        new HtmlWebpackPlugin({
            inject: true,
            template: "src/index.html"
        }),
        new MiniCssExtractPlugin({
            filename: "style.css"
        })
    ],

    // cloud9用の設定
    devServer: {
        contentBase: path.resolve(__dirname, "dist"), // dist以下のファイルを監視
        port: 8080,
        host: "0.0.0.0",
        disableHostCheck: true
    }
};
```

### devTool
...デベロッパーツールの設定です。ここに「source-map」を指定しないと先ほどのtsconfig.jsonにてsourceMapを有効にしていてもビルドされません。

### entry
...ビルド時の起点となるファイルです。エントリポイントと呼びます。
このファイルからモジュール等の依存関係を整理して後述のoutputで指定した形式でファイルに出力します。

### output
...ビルドしたファイルの形式を指定します。
今回の設定では、「dist」というフォルダに「(元ファイル名).bundle.js」という名前で保存するようにしています。

### resolve.extensions
...モジュールの解決に使用される拡張子の配列を指定します。

### plugins
...拡張プラグインを追加します。

#### html-webpack-plugin
...コンパイル時にdist以下にindex.htmlも生成させる為に使用しました。
dist以下をgit管理したくなかったのでsrc以下にindex.htmlを配置しビルド時に一緒に生成するようにしています。

#### mini-css-extract-plugin
...コンパイル時にscssファイルをcssとして生成する為に使用しました。

### devServer
...cloud9動作確認用の設定です。
dist以下に変更が加わった際にリロードがかかる(はず)

# 動作確認
上記必要な設定ファイルが用意できたら動作確認を進めていきます。
cloud9上であれば以下のコマンドとコンソール画面に対するアクションで確認することができます。

```
$ cd react-app
$ npm start
```

### コンソール画面におけるアクション
```
「Preview」 => 「Preview Running Application」
```

<img width="1440" alt="screenshot 2020-01-05 11.35.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46053/b145094e-8768-2866-f97a-292f63559d06.png">
↑Reactのチュートリアルで作成したアプリです。(○×ゲーム)

ビルドを実行すればdistフォルダとビルドの成果物が作成されます。

```
$ npm run build
```

# さいごに
今までLaravel等のフレームワーク(以下、FW)を利用していて、FWがよしなにやってくれていることがたくさんあることは理解していました。
2020年は、基本的にはFWが頑張ってくれている部分を勉強する年にしたいと思っての備忘録をアップしていければと思っております。
間違い等ございましたらご指摘くださいますと幸いです。

次回は、先ほど作ったアプリケーションをS3に配置して公開してみようと思います。