【webpack v5】React+webpackでHello Worldを表示するまで

結構前に「[React+webpackでHello Worldを表示するまで](https://qiita.com/egcmrsk/items/87e2c47856c6df66b177)」という記事を書いたのですが、あれからだいぶ環境が変わったので、改めて投稿します。

もし「ここおかしいよ！」って部分があればご指摘いただけるとありがたいです。

#環境
Node.js 15.0.1
npm 7.6.0

#下準備
ディレクトリを作成し、```npm init```するところまでは一緒です。

```
mkdir project-name
cd project-name
npm init
```

#インストール
必要なものをインストールします。
~~以前はwebpack-dev-serverを入れていましたが、webpack v5だとまだ対応していないようで、動かなかったため、今回は入れません。~~
~~代わりにwebpack-cli serveを使用します。~~

※2021/09/29
webpack-cli serveを使用する際もwebpack-dev-serverを入れる必要があるようです。

```shell
#webpack
npm install --save-dev webpack webpack-cli webpack-dev-server

#babel
npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/preset-react babel-loader

#react
npm install --save-dev react react-dom
```

TypeScript使いたいのでこれもインストールします。

```shell
npm install --save-dev typescript ts-loader
npm install --save-dev @babel/preset-typescript @types/react @types/react-dom
```

スタイルシート関連です。
mini-css-extract-pluginはCSSのみ別ファイルに書き出すために必要。

```shell
npm install --save-dev css-loader sass-loader style-loader node-sass
npm install --save-dev mini-css-extract-plugin
```

#webpackの設定
webpack.config.js を作成し、以下を記述します。
コンポーネントなどのファイルを保存するディレクトリが「src」、ローカルサーバー用のディレクトリが「public」、ビルドしたファイルの保存先が「dist」です。
以前はbabelの設定を別ファイルにしていましたが、こちらにまとめています。

modeは適宜書き換えてください。
watchオプション入れていますが、こちらも特に必要ない場合はfalseで。

※2021/09/29
devServerの設定を修正

```js:webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  mode: 'development',
  devtool: 'source-map',
  entry: {
    app: './src/index.tsx'
  },
  output: {
    filename: 'js/[name].bundle.js',
    path: `${__dirname}/dist`
  },
  module: {
    rules: [
      {
        test: /\.ts[x]?$/,
        exclude: /node_modules/,
        use: 'ts-loader'
      }, {
        test: /\.(sa|sc|c)ss$/,
        exclude: /node_modules/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: { url: false }
          },
          'sass-loader'
        ]
      }
    ]
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.json']
  },
  target: ['web', 'es6'],
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/app.css'
    })
  ],
  watch: true,
  watchOptions: {
    ignored: /node_modules/
  },
  devServer: {
    static: {
        directory: `${__dirname}/public`
    },
    compress: true,
    port: 8080,
    open: true
    // open: true,
    // contentBase: `${__dirname}/public`
  }
};
```

#package.jsonの設定
コマンドでwebpackのどの機能を実行するかを設定します。
```npm start```でローカルサーバー起動、```npm run build```でビルド出来るようになります。
```--mode development```の部分は適宜書き換えてください。

```json:package.json
{
  "name": "project-name",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    // ここを編集
    "build": "webpack",
    "start": "webpack-cli serve --mode development",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  ...
}
```

#TypeScriptの設定
TypeScriptのための設定ファイルを用意します。
正直オプション多すぎてよくわかっていません...

```tsconfig.json
{
  "compilerOptions": {
    "sourceMap": true,
    "target": "es6",
    "module": "es2015",
    "jsx": "react",
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "lib": [
      "es2020",
      "dom"
    ],
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true
  },
  "include": [
    "**/*.ts",
    "**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}
```

#Hello Worldを表示
表示用のテンプレートを作成します。
JS、CSSは自動で読み込まれないので、webpackで設定した名前のファイルを読み込むよう記述します。

```html:public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <!-- ビルドするCSSファイル -->
  <link rel="stylesheet" href="css/app.css">
  <!-- 追加で読み込みたい場合は、public内に保存して読み込む -->
  <link rel="stylesheet" href="css/plugins.css">
</head>
<body>
  <div id="root"></div>
  <!-- ビルドするJSファイル -->
  <script src="js/app.bundle.js"></script>
</body>
</html>
```

reactで文字を表示。

```js:src/index.tsx
import React from "react"
import ReactDOM from "react-dom"

ReactDOM.render(
  <h1>Hello World!</h1>,
  document.getElementById("root")
)
```

```npm start```でサーバーを起動して、http://localhost:8080 にアクセスします。
「Hello World!」と表示されれば完了です。

#最終的な構成
src/components内にコンポーネントを保存して、index.tsxでインポートします。

```
├─ dist // ビルドしたファイルの保存先
├─ node-modules
├─ public // ローカルサーバー用のファイル
│   ├─ index.html
├─ src
│   ├─ components // 作成したコンポーネントを保存
│   └─ index.tsx
├─ package.json
├─ package-lock.json
├─ tsconfig.json
└─ webpack.config.js
```

#おまけ
##ファイルを分割する
よく１つのファイルにまとめて書き出そうとして「重いよ！」って怒られるので、分割します。
ビルドすると、app.bundle.jsとplugins.bundle.jsが作成されます。

```js:webpack.config.js
module.exports = {
  ...
  entry: {
    app: './src/index.tsx',
    plugins: './src/plugins.tsx'
  },
  output: {
    filename: 'js/[name].bundle.js',
    path: `${__dirname}/dist`
  },
  ...
```

#参考
参考にさせていただきました。ありがとうございます。

[tsconfig.jsonの全オプションを理解する（随時追加中）](https://qiita.com/ryokkkke/items/390647a7c26933940470)
[webpack.config.js の書き方をしっかり理解しよう](https://qiita.com/ginokinh/items/d4e1a9134c215d0ea701)
[webpack v5 npx webpack-dev-server で dev server が動かないにハマる](https://chaika.hatenablog.com/entry/2020/11/23/083000)