create-react-appコマンド無しでcreate-react-appっぽいreact + typescriptの環境構築していきます。 今回、create-react-appぽいとは次のようなことを指します。
- とりあえずts,tsxが動く
- yarn buildでbuildファイルを生成する。
- yarn startでローカルサーバーが自動で起動する。
- ローカルサーバーが起動中、編集すると、自動でリロードされる(ホットリロード)
登場人物ややこしすぎ問題
とにかく構築にいろいろなパターンが多い + デファクトがよくわからない。1
本記事ではwebpack + ts-loaderの構成で構築していこうと思います。(babel-loaderを使わないパターンです)
そのため、対象となる読者は以下のとおりです。
・始めたてで、とにかく動かしてみたいだけ。
・細かいpolyfillは考えていない。
・作るものが小規模だ。(個人の練習アプリ程度)
##環境
環境は以下の通りです。yarn使います。
$ node -v
v13.11.0
$ yarn -v
1.22.4
init
まずはじめにpackage.jsonを生成します。
$ yarn init -y //-yで対話をスキップ
##パッケージのインストール
次にwebpackともろもろのインストールを行います。
$ yarn add -D webpack@4.43.0 webpack-cli@3.3.12 webpack-dev-server@3.11.0 html-webpack-plugin typescript ts-loader
正常な動作を確認しているのは上記のバージョンのwebpack,webpack-cli,webpack-dev-serverのみです。バージョンを指定しない場合webpack v5系がインストールされるので注意してください。(2020年11/21日現在)2
- webpack ・・・・みんな大好きモジュールバンドラーの本体
- webpack-cli ・・・・webpackコマンドがshellで使える様になる。package.jsonのscriptsにこのコマンドを割り当てることが多い
- webpack-dev-server ・・・・開発サーバー用
- html-webpack-plugin ・・・・必須ではないです。webpackがバンドルと一緒に.htmlも吐き出してくれます。自前でhtmlファイルを用意してもいいですが、使い方がとても簡単かつ楽なので筆者はいつもwebpackにまかせてます。
- typescript ・・・・いつものあいつ
- ts-loader ・・・・webpackがバンドルを作成するときにtsx—> jsx —> jsとコンパイルしてくれます。babel-loaderとごっちゃになってハゲそうになった。
scriptsの設定
先にpackage.jsonにscriptsを設定しておきましょう。
"scripts":{
// dev-serverをdevelopmentモードで起動
"start": "webpack-dev-server --config ./webpack.config.js --mode development”,
// productionモードでbuildファイルの出力
"build":"webpack --mode production"
}
reactのインストール
次にreactと型定義ファイルをインストールします。こちらの詳細は割愛します。
$ yarn add react react-dom
$ yarn add -D @types/react @types/react-dom
ここから諸々のファイルを作成していきます。最終的なディレクトリ構成を載せておきます。
├── build //buildディレクトリ
│ ├── bundle.js
│ └── index.html
├── package.json
├── src
│ ├── index.tsx //エントリーポイント
│ └── template // htmlpluginのテンプレート
│ └── index.html
├── tsconfig.json
├── webpack.config.js
└── yarn.lock
webpack.config.js
webpack.config.jsを作成します。
// webpack.config.js
const path = require("path");
//htmlプラグインのための呪文
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "development",
entry: {
bundle: "./src/index.tsx", // エントリーファイルの位置
},
output: {
filename: "[name].js", // 今回はbundle.jsで吐き出される(entryで指定)
path: path.join(__dirname, "build"), // buildという名前のbuildディレクトリを吐き出す
chunkFilename: "[name].js",//今回は触れません
},
devServer: {
contentBase: path.join(__dirname, "build"),
compress: true,
port: 8080,
open: true, //yarn start コマンドを入力すると自動でブラウザに遷移します。
historyApiFallback: true,
},
module: {
rules: [
{
test: /\.ts(x?)$/, //.ts または.tsxに対して
loader: "ts-loader", // ts-loaderを使用
},
],
},
resolve: {
extensions: [".ts", ".js", ".tsx", ".jsx"],
},
plugins: [
new HtmlWebpackPlugin({
template: `${__dirname}/src/template/index.html`, //テンプレートファイルの場所
filename: "index.html", //テンプレートファイルの名前
inject: "body", //bodyタグの直前でwebpackのバンドルを埋め込み
}),
],
};
おまけ: 本当はwebpackのprodモードとdevモードの環境は個別に設定することが推奨されています。詳しくは公式ドキュメントを参照してください。今回はscriptsで無理やり分けてます。
tsconfig.json
次にtsconfig.jsonを作成します。ts-loaderはこのファイルの設定に従います。
必ず "jsx":"react"を指定しましょう。
ほぼデフォルトのコピペです。細かいのはお好みで。
// tsconfig.json
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"module": "esnext",
"resolveJsonModule": true,
/* Basic Options */
"allowJs": false /* Allow javascript files to be compiled. */,
"jsx": "react" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */,
"sourceMap": true /* Generates corresponding '.map' file. */,
"outDir": "/build" /* Redirect output structure to the directory. */,
"isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */,
/* Strict Type-Checking Options */
"strict": true /* Enable all strict type-checking options. */,
"noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
"strictNullChecks": true /* Enable strict null checks. */,
/* Additional Checks */
"noUnusedLocals": true /* Report errors on unused locals. */,
"noUnusedParameters": true /* Report errors on unused parameters. */,
"noImplicitReturns": true /* Report error when not all code paths in function return a value. */,
"noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */,
/* Module Resolution Options */
"moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
relative to the 'baseUrl'. */
structure of the project at runtime. */
"allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */,
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
/* Advanced Options */
"skipLibCheck": true /* Skip type checking of declaration files. */,
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
},
"include": ["src"]
}
仕上げ
.tsxとhtmlのテンプレートを作成します。
// src/index.tsx
import React from "react";
import ReactDOM from "react-dom";
ReactDOM.render(<h1>hello</h1>, document.getElementById("root"));
// src/template/index.html
// ! + tab キーで作ってます。
// <div id = "root"></div>を忘れずに
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id='root'></div>
</body>
</html>
ここで、
$ yarn start
するとindex.tsxの内容が描画されると思います。
ソースコードはこちら