React + Viteの導入の手順メモ
はじめに
React + Videの導入について色々と調査しています。 (2024年1月版)
不明なこともあるので変なところがあるかもしれません。
- yarn
- VSCode
- 開発Windows
- デプロイLinux ubuntu22.02
事前準備
VSCodeのインストール
特に悩みなく公式URLからインストール
Node.jsのインストール
以下のURLからNode.jsのLTS版をインストール
Yarnのインストール
次のURLからインストール
日本語のURLはNot Foundであった。
インストール後の確認
いずれもGIT Bash上で実行
$ node -v
v20.10.0
$ npm -v
10.2.3
$ yarn -v
1.22.19
Reactのプロジェクトの作成
yarnでReactのTypeScriptで作成を実施する。
yarn create vite my-project --template react-ts
作成後次のコマンドを実行する。
cd my-project
yarn
開発用の起動
- 開発時は次のコマンドで起動する
yarn dev
- デフォルトだと次のURLでHTTPが起動するのでブラウザでアクセス可能
- http://localhost:5173/
- この起動の場合はソースを修正してもViteが頑張ってくれて、自動で反映される。
- --hostオプションを付けると他のPCからもアクセス可能
デプロイ
- デプロイ時のファイルのビルドは以下のコマンドで実行するように調整している。
yarn build --mode development
yarn build --mode production # or yarn build
yarn build --mode staging
yarn build --mode release
- 作成されたファイルをnginxやApache配下に配置する。
- ローカル開発時、チーム開発時(development|production)、ステージング、リリースの4環境になる。
- ※--modeの指定とNODE_ENVは別の概念であることに注意が必要
- 結果ファイルは/distディレクトリに出力される。
- この出力ディレクトリを--mode毎にする方法がほしいところだが、CI/CDを使う場合は固定の方がよさそうな感じである。
環境毎の設定
- 開発時の環境変数設定・・・.env.development
-
yarn dev
とyarn build --mode development
時が対象
NODE_ENV=development
VITE_TEST1=A
- 結合時の環境変数設定・・・.env.production
-
yarn build
とyarn build --mode production
時が対象 - NODE_ENV=productionは指定が不要なのでコメントアウトしているが判別用に記載している
#NODE_ENV=production
VITE_TEST1=B
- ステージングの環境変数設定・・・.env.staging
-
yarn build --mode staging
が対象 - NODE_ENV=productionは指定が不要なのでコメントアウトしているが判別用に記載している
#NODE_ENV=production
VITE_TEST1=C
- リリース時の環境変数設定・・・.env.release
-
yarn build --mode release
が対象 - NODE_ENV=productionは指定が不要なのでコメントアウトしているが判別用に記載している
#NODE_ENV=production
VITE_TEST1=D
- .envファイルもyarn dev起動時はホットリロード対象で、再起動不要で反映された。
確認時のApp.tsxの内容
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
function App() {
const [count, setCount] = useState(0)
return (
<>
<div>
<a href="https://vitejs.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
TEST count is {count}
</button>
<p>
Edit <code>src/App.tsx</code> and save to test HMR
</p>
</div>
<p>MODE=
{import.meta.env.MODE}
</p>
<p>PROD=
{import.meta.env.PROD ? "true" : "false"}
</p>
<p>DEV=
{import.meta.env.DEV ? "true" : "false"}
</p>
<p>VITE_TEST1=
{import.meta.env.VITE_TEST1}
</p>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
)
}
export default App
基本的な追加モジュール
Router
yarn add react-router-dom
HTTPリクエストの実施用
yarn add axios
デプロイ時の注意点
- デプロイ方法は多々あるので、こちらはその一例になります。
- 単にdist/に出力されたファイルをdocument_root直下にコピーするだけです。
- nginxを利用しています。
- サブディレクトリは未使用です。
- サブディレクトリに配置したい場合は、いくつか特別な対応が必要になります。
- 個人的には可能な限りサブディレクトリ配置はさけて、サブドメインを利用して/からアクセスさせる方が良いとは思います。
- それはそれでCORSなどの問題が出ることはありますが、サブディレクトリ配置は慣れてきたらがよさそうです。
- 仮にフロントエンドとバックエンドを単一ドメインでデプロイする場合、静的コンテンツやAPI側をサブディレクトリにするほうが簡単です。
- nginxでlocationのパス毎に異なるrootを設定してしまえばよさそうでした。具体的には、以下ような設定になります。
location /api/ {
root /var/www/api_files/
# 注意点としては/var/www/api_files/api/配下にファイルを配置する必要がある
# CGIではなく、FastAPIやFlaskの場合はリバースプロキシ設定になる。パス構造の維持は同様
}
location /static/ {
root /var/www/static_files/
# 注意点としては/var/www/static_files/static/配下にファイルを配置する必要がある
}
location / {
root /var/www/react_dist/
# ここにbuildでdistに出力されたファイルを配置する
}
その他①・・・動機
フロントエンドはReact + Vite、バックエンドはFlask or FastAPIという構成を挑戦してみています。
個人的にはバックエンドもTypeScript(Javascript)が利用できるnuxt.jsやnext.jsといったフレームワークは、今後の課題としています。
これはフロントエンド側とバックエンド側は同一言語じゃないほうが自分には向いているなという感想ではあります。(そのうちこの意見は変わるかも)
まだ慣れていないので、同一コードでもフロントエンドとバックエンドで動く条件が変わるので、カット&ペーストしていると思わぬ落とし穴にハマることがあるためと、ビルド&デプロイや環境変数周りに違和感が消えていない状況です。
また、サーバサイドはバッチ処理や、管理コマンドなどを作ることがあり、その用途にはNode.jsは若干やりにくいところのあるので、クライアントサイドとサーバサイドで言語は分けたほうがいいかなという段階ではいます。
その他②・・・環境ごとのビルドの選択
標準のdevelopmentとproductionは個人的には少しズレを感じています。
経験的には、最小で4環境の5 or 6設定にならざるを得ないことが多いです。
- ①
yarn dev
による開発時・・・個人ローカル開発時 - ②
yarn build --development
で、開発環境へのデプロイ時・・・チーム内の結合での開発時(序盤~中盤) - ③
yarn build
oryarn build --production
で、開発環境へのデプロイ時・・・チーム内結合での開発時で、リリース時と互換設定での動作確認(中盤~終盤) - ④
yarn build --staging
で、ステージング環境へのデプロイ時・・・結合テスト&リリース前テスト時 - ⑤
yarn build --release
で、リリース環境へのデプロイ時・・・リリース時
①と②の環境ズレが生じだしたらもう一個追加します。
③を抜かして、②を①でやると、大抵④で大きく躓きます。
これは、yarn dev
とそれ以外は似て非なるものであるためです。
⑤に対してyarn build
に割り当てようとすると、release環境が複数になる場合に手間が生じるのと、リリースビルドに曖昧性のある未指定のデフォルトを使いたくないというのがあります。
この環境切り替えについてはベストプラクティスは模索中ですが、CI/CDやGITのブランチ管理もからむので方言が多いとはおもっています。
調べているとdevとproductionだけにして、その.env.productionの中身を環境ごとに別の手段で、書き換えているのが多い感じはしていますが、どのようにして書き換えている設定を管理しているか?という問題にはなっていきます。
感想
「Reactについては.envによる環境変数はbuild時に反映される。」
これはあたりまえですが失念してはならない特徴かなと思っていて、ビルド後のdistファイルの静的配置の場合、実行時の環境変数で変更することはできないとの認識です。
yarn dev
で開発しているとホットリロードされるのですが、それが特別な挙動であることが要注意なところと考えています。
どちらかというとC++などでいう#defineなどのプリプロセッサに近い挙動です。
(Nuxt.jsやNext.jsでNode.jsがバックエンドの場合に、サーバサイドのコードではprocess.envは実行時に変化する一般的な意味での環境変数と言える動作のようです。)
クライアント側に出ていくのでパスワードのようなものを入れる可能性はないのですが(VITE_をプレフィックスとして限定しているのも、この辺も理由でしょうかね)、それでも環境毎に関連する固有の設定をビルド時に持っていないとならないので、.envで色々調整しようとすると本番環境独自の設定をビルド時にも把握しないとならなくなります。
それを回避するため、dockerのコンテナのイメージビルド時にすでに作成しておいたdist/をコピーするのではなく、ソースをコピーして、イメージビルド時にReactビルドや、デプロイされたコンテナの起動時にコンテナ内でReactビルドしてdocument_rootに配置なんていったケースもあるとは思います。
(後者ではイメージは共用でデプロイ時の設定で挙動を変更できるが、ソースがイメージに含まれることになるのと起動時間がかかる)
yarnによるパッケージダウンロードとかは、DockerImageビルド時に実施しておくと効率化されます。
なんか違和感があるところで、やはりイメージや本番環境への配置はdist/配下のファイルだけがベストではないかと考えてはいますが、イメージ内にビルド用の環境を全部を抱えてしまうケースというのは検索すると出てきます。
(Dockerfile内にfromを複数記載するマルチステージビルドであっても)
あとは、React側はどの環境でもほとんど同じ設定になるように実装して、環境毎に関連する設定はREST APIで起動時にバックエンドから取得するというのがよいのかな?とも思ったりはします。
この辺はまだまだ調査&模索しているところとなります。なんか勘違いがあるのかもしれません。
Dockerfileの例とかみると、C++でいえば、コンパイラやインクルードファイルとかライブラリといったビルド環境自体をフルセットで保持するイメージをゼロから一回作って、そこでビルドして、生成されたdistファイルだけをデプロイイメージに配置しコンテナとして起動するケースが多いようです。
ビルドしたイメージをDockerレジストリに登録して共用し、コンテナデプロイ時の環境変数で動作をわけるというより、環境毎にゼロからイメージのビルド&デプロイするということが想定ケースにはみえます。それであると環境変数のズレや多重管理は無くせそうです。
このあたりを見ると、ビルド環境で作ったイメージをレジストリに登録し、それを共用してデプロイ時の環境変数で挙動を変更するのはメジャーではなく、「環境毎にイメージが作成されるようにして、.envを変えたらビルドからやり直す」が標準なのかなという印象ではあります。
この辺は自分の中でDockerを使う場合と、Dockerを利用しない場合がまだ上手く整理しきれていないのかなとは思っています。
Discussion