はじめに
この記事ではcreate-react-appに頼ることなくreactの環境を作成する。dockerで構築しているが、手元にnode環境があれば飛ばしても良い。
完成後の構成
環境構築が終わると以下のようなフォルダ構成になる。
.
├── dist
│ ├── index.html
│ └── index.js
├── docker
│ └── node
│ └── Dockerfile
├── node_modules
├── src
│ ├── main.tsx
│ └── sample-component.tsx
└── docker-compose.yml
└── package-lock.json
└── package.json
└── tsconfig.json
└── webpack.config.js
dockerの環境設定
dockerではnodeの環境を構築する。
nodeのDokcefile
は以下の通り
FROM node:16-alpine
WORKDIR /var/www/app
WORKDIRは適当に設定した。nodeのバージョンは16-alpine
としたが、ここから自由に選んで良い(動作確認は16-alpineでしか行ってない)。
docker-compose.yml
は以下の通り
version: "3.8"
services:
node:
build: ./docker/node
tty: true
volumes:
- ./:/var/www/app
working_dir: /var/www/app
ports:
- 3000:3000
Dokcerfileとdocker-compose.ymlに分けたが、まとめても問題ない。portsなど細かい設定は任意に変えて良い。これらを設定したら、docker-compose up --build -d
でDocker立ち上げると、Dockerのnode環境は完成。
Reactの環境設定
Reactの環境設定はdocker-compose exec node sh
でnodeのDokcerに入って全て行う(今後行うコマンド全て)。dockerを使わない場合はシェルにそのまま打てば良い。
プロジェクトの初期化
npmプロジェクトの立ち上げとして
npm init -y
と入力する。そうすると
{
"name": "app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
のようなファイルが生成される。立ち上げの際に詳細を設定したい場合は代わりにnpm init
と入力して設定する。
必要なパッケージのインストール
今回必要最低限のパッケージとして以下のものをインストールする。
- webpack: JSモジュールのバンドラー
- webpack-cli: webpackのCLI
- typescript
- ts-loader: typescriptをwebpackで処理するのに必要
- react
- react-dom: DOMを管理するもの
- @types/react: reactの型定義
- @types/react-dom: react-domの型定義
まず、reactとreact-dom以外は開発専用のパッケージなので
npm i -D webpack webpack-cli typescript ts-loader @types/react @types/react-dom
のようにインストールする。reactとreact-domは普通に
npm i react react-dom
のようにインストールする(nodeのバージョンが低ければオプションに-Sが必要)。今後必要なライブラリがあれば上記のようにインストールするとよい。
このようにインストールすることでpackage.json
は
{
"name": "app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/react": "^17.0.20",
"@types/react-dom": "^17.0.9",
"ts-loader": "^9.2.5",
"typescript": "^4.4.2",
"webpack": "^5.52.0",
"webpack-cli": "^4.8.0"
},
"dependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
}
}
のように変更される。その他にもpackage-lock.jsonやnode_modulesが生成される。
typescriptの設定
typescriptの設定はtsconfig.json
に書く。
{
"compilerOptions": {
"sourceMap": true,
"target": "ES6",
"module": "ES2020",
"jsx": "react",
"moduleResolution": "node",
"lib": [
"ES2020",
"DOM"
]
}
}
tsconfigの設定は公式が見やすいのでこれを見て変更するとよい。何も考えずに動かす分には上記のものを使用すると良い。
webpackの構成
const path = require("path");
module.exports = {
mode: "development",
entry: "./src/main.tsx",
output: {
filename: "main.js",
path: path.resolve("dist"),
publicPath: "/",
},
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader"
}
]
},
resolve: {
extensions: [".ts", ".tsx", ".js", ".json"]
},
};
この記事ではts,tsxファイルをsrc
に置き、その中でもメインファイルをmain.tsx
としているのでentry
は"./src/main.tsx"
となった。また、出力先をdist
、コンパイル後のファイル名をindex.js
としたので、output
は
{
filename: "main.js",
path: path.resolve("dist"),
publicPath: "/",
},
となった。module
ではts-loaderを使用する条件が書かれており、ts,tsxファイルに対して使用される。resolve
はimport文の名前解決のために記述した。
typescriptのビルドコマンド追加
実際に上記で設定通りにts,tsxファイルをコンパイルするコマンドをpackage.json
のscriptに追加する。
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack",
"watch": "webpack -w"
},
このように追加することでnpm run build
でビルド、npm run watch
でホットリロードが可能となる。その他scriptを追加するときはここに追加することとなる。これらのコマンドによってsrc
内のmain.tsx
がdist
にindex.js
としてコンパイルされる。
実際に動かしてみる
テンプレートとして静的ファイルであるindex.html
をdist
に作成する。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<script defer src="./main.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
scriptでコンパイル後のjsファイルを読み込んでいる。<div id="app"></div>
を埋め込む場所にReactが反映される(appでなくても良い)。
次にsrc
にtypescriptで書かれたreactのファイルを配置する。サンプルとして簡単なファイル用意した。webpack.config.js
のresolveがうまく動いていることを確認するためにtsxのファイルを二個作成した(example-component.tsxがうまくimportされるかを確かめる)。
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { SampleComponent } from './sample-component';
class App extends React.Component {
render() {
return (
<div>
<h1>React Example</h1>
<SampleComponent name="Counter"/>
</div>
);
}
}
ReactDOM.render(<App/>, document.querySelector('#app'));
最終行のappがindex.html
のdivのid(app)に対応している。
import * as React from 'react';
interface IProps {
name: string;
}
export const SampleComponent = (props: IProps): React.ReactElement => {
const [count, setCount] = React.useState<number>(0);
return (
<div>
<h1>{props.name}</h1>
<div>{count}</div>
<button onClick={() => setCount(count + 1)}>Count Up</button>
</div>
);
}
上記の三つのファイルを作成したのちにnpm run dev
を行い、./dist/index.js
にファイルが生成されたのを確認したら、index.html
を開くと
こんな感じの簡単なカウンターが現れる。