作成日:2022年7月5日
reactのチュートリアルから誰もがお世話になるcreate-react-app。
reactのアプリケーションをコマンド一発で楽々に作ることができますよね。
便利だし、早いし、typescriptの環境構築も楽勝です。
めちゃくちゃ便利なだけあってreact初心者は、
自動でインストールされているwebpack等の関連ライブラリを意識できません。
create-react-appを使用して開発を始めると、「package.json??なんやそれ、、怖いから触らないでおこう」的な軽いノリで、
モダンJSにおいて大事な部分を見て見ぬふりをしながら、reactを触り始めることができてしまいます。
今回は、create-react-appを使わずにreact環境構築をして、
いろいろすっ飛ばしてきた部分を理解しつつ、create-react-appと遜色ない環境を作っていきます。
npmで各種パッケージをインストールし、環境構築していきます。
※Node.Js、npmをインストールできていない方は下記を参考にしてください。
Node.js、npmのインストール方法|Qiita
プロジェクト名は「react-forum」とします。
#「react-forum」フォルダの作成
$ mkdir react-forum
#react-forumフォルダ直下に移動
$ cd react-forum
package.jsonの中にはインストールしたパッケージがJson形式で記録されています。
実行すると、コマンドラインでいろいろ聞かれると思いますが全部エンターを押して飛ばしてもらって問題ないです。
#package.jsonを作成
$ npm init
ここまではお約束といった感じですね。
webpackの主な役割を簡単に説明すると、
複数のJavascriptやcss等のモジュールを一つのjsファイルにまとめてくれるライブラリです。
Webpackについては、下記で詳しく説明しておりますので、もっと知りたい方は下記を参考にしてください。
Webpackのパッケージを二つインストールします。
$ npm install webpack webpack-cli
ここで package.jsonの中身を確認してみましょう。package.json
{
"name": "react-forum",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
//この中にインストールしたパッケージ名とバージョンが追加されていきます。
"devDependencies": {
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0"
}
}
devDependenciesにwebpack、webpack-cliが追記されていることが確認できれば問題ないです。
ちなみに、先ほどののインストールは「--save-dev」というオプションを付けましたが、これを付けた場合、
開発環境でのみ使うことができるパッケージとしてインストールされます。
「--save-dev」を付けなかった場合本番環境、開発環境両方で使えるパッケージとなり、
package.jsonのdependenciesに追加されます。
webpackはローカルでビルドする際にしか使用しないので、「--save-dev」オプションをつけてインストールします。
「webpack.config.js」はwebpackの設定ができるJsファイルです。
入出力ファイルの設定はもちろんのこと、ビルド時のカスタマイズなど様々な設定ができます。
ルートディレクトリにwebpack.config.jsを作成します。
$ mkdir webpack.config.js
今回は出力ファイルを「dist/static/main.js」としています。
各設定項目の説明はコメントに記載。webpack.config.js
const path = require('path')
module.exports = {
// 'development'に設定すると、バンドルファイルに「元のコード」と「変換後のコード」との
// 対応関係が記述されたコメントが記述されます
// 'production'に設定すると、コメントは記述されず、最適化されたコードで変換されます
mode: 'development',
// 最初に実行する変換前のファイル
// 今回はsrcフォルダのindex.tsを指定
entry: './src/index.ts',
//バンドルファイルの出力設定
//__dirnameでプロジェクトのルートパスを取得できます。
output: {
path: path.join(__dirname, '/dist/static'),
filename: 'main.js' //出力ファイル名
},
//importの際に省略する拡張子をextension:配列で指定
resolve: {
extensions: ['.ts', '.tsx', '.js']
},
};
reactに限らず、webpack+typescriptの開発環境を作る上で必要な手順は下記のようになります。
まずは1,2のモジュールをインストールします。
$ npm install --save-dev typescript ts-loader
次にトランスパイルをどのように行うかの設定を行います。
トランスパイルの設定を行います。
$ mkdir tsconfig.json
tsconfig.json
{
"compilerOptions": {
"target": "es5",//出力するjavascriptのバージョン
"jsx": "react",//jsxをReact.createElementに変換する設定。jsxのままにするような場合は"preserve"で指定
"strict": true,//trueにするとtypescriptのコンパイル時の型チェックがより厳格になる。
"esModuleInterop": true,//CommonJSとESモジュール間との相互運用を可能にするためのフラグです。積極的にtrueで大丈夫です。
"removeComments": true,//出力ファイルのコメントを削除するかどうか
"skipLibCheck": true,//.d.tsファイルの型チェックをスキップする。
"forceConsistentCasingInFileNames": true//importするファイル名の大文字小文字を区別するかどうか。
}
}
最後に、4の手順です。
webpack.config.jsにmodule.rulesを追加し、「.ts」「.tsx」ファイルに対してts-loaderを使ってトランスパイルする設定します。webpack.config.js
const path = require('path')
module.exports = {
mode: 'development',
entry: './src/index.ts',
output: {
path: path.join(__dirname, '/dist/static'),
filename: 'main.js' //出力ファイル名
},
//追加
module: {
rules: [
{
// 「.ts 」または「.tsx 」ファイルを変換するよ~
test: /\.(ts|tsx)$/,
// ts-loaderを使って変換するよ~
use: 'ts-loader',
},
],
}
resolve: {
extensions: ['.ts', '.tsx', '.js']
},
};
Reactを使うためにはreact本体と、react-domの二種類のパッケージをインストールする必要があります。
react本体には、おなじみのreact機能が入っていて、
react-domはreactとDOMを橋渡しするような役割があります。
$ npm install react react-dom
せっかくtypescriptを使っているのにreactのtypesがないことには意味がありません。
$ npm install @types/react @types/react-dom
@types/reactの中身に興味がある方はnode_modules/@types/react内の「index.d.js」、「global.d.js」のソースを見てみることもお勧めします。
ここまでで
typescriptでreact開発
↓
webpackでバンドル
までの一連の流れを行う設定はできています。
あとは、reactの中身を書いて、webpackの出力ファイル(デフォルトで/dist/main.js)
をhtmlで読み込んで表示していきます。
webpack実行時のエントリポイントとなる「src/index.ts」と、親コンポーネントとなるApp.tsxを作成をします。
srcディレクトリを作成します。
$ mkdir src
srcディレクトリの中にファイルを二つ作成します。
(どうやらreactのv18以降はrender()は使えないみたいなので、createRoot()を使ってます。)index.ts
import React from "react";
import { createRoot } from 'react-dom/client';
import App from "./app"
const ROOT_ID = "root" as const ;//次に作成するdist/index.htmlのルート要素のid
const rootElement = document.getElementById(ROOT_ID);
//rootElementは HTMLElement | null なので、nullの場合の処理を書かないと怒られます
if (!rootElement) throw new Error(`${ROOT_ID}の要素が見つかりません。`);
const root = createRoot(rootElement);
root.render(<App/>);
app.tsx
import React from 'react'
function App() {
return (
<h1>Hello World!!</h1>
)
}
export default App
webpack実行時の出力ファイルはdist/static/main.jsなので、distフォルダを作成し、
dist直下にindex.htmlを作ってあげます。
reactをレンダリングする部分にはindex.jsでも指定した、id="root"を設定します。
バンドルファイル(static/main.js)はhtml読み込み後に実行しなければならないので、
defer属性を付与することを忘れないようにしてください。dist/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script defer src="static/main.js"></script>
<title>掲示板</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
ここまで出来たらあとはwebpackを実行すれば、src配下のファイルがdist/static/main.jsにバンドルされます!
実行する際はnpm の場合パスが通っていないので、npxを使用します。
(npxで実行した場合には、node_modules/.bin配下から実行するファイルを選んで実行してくれます。)
$ npx webpack
npmで実行したい場合は、package.jsonファイルのscriptにビルドコマンドを追加し、実行します。package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build":"webpack"
},
$ npm run build
dist/static/main.jsが作成されていれば成功です。
エクスプローラからdist/index.htmlを実行してみましょう。
成功!!!
でも毎度毎度コマンドをたたいてブラウザを確認するのは不便ですよね。
ここからはローカルサーバーを使ってよりサクサクテストできる環境を作っていきます。
webpack-dev-serverを使えば、保存時に変更の差分のみバンドルファイルに即時反映され、効率よく開発を行うことができます。
webpack-dev-serverをインストールします。
※「webpack-devserver」という似て非なるパッケージもあるので、インストールする際は気を付けてください。
$ npm install --save-dev webpack-dev-server
続いて、webpack-dev-serverの設定を行っていきます。
webpack.config.jsでdevserverオプションを設定することで、webpack-dev-serverの起動時の挙動や、サーバーの設定が可能です。webpack.config.js
//webpack-dev-serverの設定
devServer: {
static: path.join(__dirname, '/dist'), //webpackの出力先を指定してあげてください
open: true, //ブラウザを自動的に起動します
port: 3000 //ポート番号を3000番に指定
},
それではwebpack-dev-serverを起動してみましょう。
$npx webpack-dev-server
ブラウザが自動で起動し、http://localhost:3000で先ほどの画面が見れれば成功です。
webpack-dev-serverを起動した状態で、src配下のファイルに何かしらの変更を加えて保存してみましょう。
変更がブラウザに反映されていないと思います。
僕はこれに引っ掛かって何時間もハマっていました。
何時間もの格闘の末、
「webpack-dev-serverはオンメモリでバンドル結果を保持していて、
そのバンドル結果はデフォルトで表示する静的ファイル直下のmain.jsからアクセスできるようになっている」
という事実が判明し、解決に至りました。
つまり、webpack-dev-serverのバンドル結果にアクセスするためのパスを変更してあげる必要があるということです。
この変更はwebpack.config.jsのoutput.publicPathから行います。webpack.config.js
output: {
path: path.join(__dirname, '/dist/static'),
publicPath: '/static/', //staticディレクトリ直下のmain.jsからバンドル結果にアクセスできるように変更
filename: 'main.js'
},
これで、ホットリロードが正常に機能するはずです。
今回は、create-react-appを使わずにtypescript+react開発の環境構築方法について解説いたしました。
webpack、typescript周りについて、まだまだ理解できていないことが多いなと反省するいいきっかけになりました。