はじめに

Hello, Swift TypeScript ラバーのみなさん。これからラバーになるみなさん。
今日も今日とて React + TypeScript のお話です。
このシリーズも早くも4回目です。以下の記事を書きました。

今回は実際に create-react-app コマンド(以下、CRA)で作成したアプリを Vite(ヴィート) に移行していきます。

世の中には複数の同様な記事がありましたが、自分のプロジェクトは素直に移行できなかったため改めて書きました。参考になれば幸いです。

それでは早速いきましょう!

移行する

環境

  • npm: 10.2.4
  • Node.js: 20.11.0
  • TypeScript: 4.9.5

使用しているライブラリ等(今回の話に出てくるやつ)

それでは移行作戦の開始です。

実践

目次

  1. 既存の CRA 関連のパッケージの削除
  2. Vite 関連のパッケージのインストール
  3. config ファイルの作成
  4. index.html の修正
  5. scripts の修正
  6. パスの修正
  7. SVG のインポート設定
  8. require インポートの廃止
  9. process.envアクセスの置き換え
  10. Tailwind CSS 設定の修正

1. 既存の CRA 関連のパッケージの削除

まずは既存の CRA 関連のパッケージの削除を行います。ご存知だとは思いますが、Git のブランチはしっかり分けておきましょう。

以下のコマンドを実行します。

npm uninstall react-scripts
npm uninstall eslint-config-react-app

上記どちらも CRA 関連のパッケージです。

  • react-scripts: CRA で使用されるスクリプトと設定が含まれているパッケージ
  • eslint-config-react-app: CRA で使用される ESLint 校正が含まれているパッケージ

2. Vite 関連のパッケージのインストール

Vite のスクリプトを動かすためにパッケージをインストールしていきます。

npm install --save-dev vite
npm install --save-dev @vitejs/plugin-react
  • vite: 今回のメインになるフロントエンドツール
  • @vitejs/plugin-react: React 用の Vite プラグイン

ここもインストールするだけ。

3. config ファイルの作成

プロジェクトルートにvite.config.mtsというファイルを作成します。

中身は以下のような設定にします。

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
    plugins: [react()],
    server: {
        port: 3000, // CRAと同じポートを使用、指定しないと5173になる
    },
    build: {
        outDir: "build", // CRAの `build` フォルダを引き継ぐ
    },
});

拡張子に関して、.mtsとしていますが、.tsでも大丈夫です。

違いとしては、以下の通りです。

  • .mts: ESM のモジュールシステムを使用する
  • .ts: CJS か ESM のモジュールシステムを使用する

Vite は基本的に ESM のモジュールシステムを使用しているため、今回は.mtsを採用しました。

4. index.html の修正

次にindex.htmlを修正します。

CRA では、/src/配下に配置されていましたが、Vite ではプロジェクトルートに配置します。

  • /src/index.html -> /index.html

そして<body />の中に以下のスクリプトを配置します。

< script type="module" src="/src/main.tsx"></script>

(htmlとして解釈されてしまったので、先頭にスペースが入っています)

また、プロジェクトルート直下に移行したことにより、いくつかのパスの修正を行います。

<link rel="apple-touch-icon" href="%PUBLIC_URL%/apple-touch-icon.png" />

↓

<link rel="apple-touch-icon" href="/apple-touch-icon.png" />


<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />

↓

<link rel="manifest" href="/manifest.json" />

CRA では public 配下を指定していたアイコンや manifest ファイルですが、Vite ではプロジェクトルートを指定します。

最終的には以下のようになります。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
    <link rel="manifest" href="/manifest.json" />
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
    < script type="module" src="/src/main.tsx"></script>
  </body>
</html>

また、< script type="module" src="/src/main.tsx"></script>とあるように、/src/main.tsxを指定しているため、CRAのindex.tsxをリネームします。

  • /srcindex.tsx -> /src/main.tsx

厳密には不要ですが、Viteではmain.tsxという名前が使用されているので、これに合わせます。

そして、DOMのレンダリング部分も以下のように修正します。

ReactDOM.createRoot(document.getElementById('root')!).render(<App />)

5. scripts の修正

react-scripts から vite に移行したので、package.json の scripts を以下のように修正します。

"scripts": {
  "start": "react-scripts start",
  "build": "react-scripts build",
}

↓

"scripts": {
  "dev": "vite",
  "build": "vite build",
  "preview": "vite preview" //ローカルでbuildしたものをプレビューするもの
}

ちなみに react-scripts の代わりは vitest などが挙げられますが、追加でインストールが必要なので、今回は触れません。

6. パスの修正

現在のプロジェクトでは、ファイル間のインポートを相対パスで参照しているため、絶対パスでの参照に変更します。

理由としては、例えばディレクトリ構成を変更した場合に相対パスであれば、インポートのパスをすべて変更する必要がありますが、絶対パスであれば、基本的に配置を変更してもインポートパスを変更する必要がないからです。

ここはいくつか修正項目があります。

package.json

"paths": {
    "@/*": ["./src/*"] // `./src/`を`@/`でアクセスできるようにする
}

vite.config.mts

import react from '@vitejs/plugin-react'
import path from 'path'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [react()],
  server: {
    port: 3000,
  },
  build: {
    outDir: 'build',
  },
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'), // Viteの設定でもパス解決する
    },
  },
})

あとは実際にファイル内の相対パス表記を絶対パス表記に書き換えます。

例えば、/src/component/screen/Chat.tsxというファイルから、/src/repository/message.tsというファイルのgetMessage()というファンクションにアクセスする場合

import { getMessage } from '../../repository/message.ts' 

↓

import { getMessage } from '@/repository/message.ts'

このようになります。

変更はめんどくさいですが、やる価値はあります。

7. SVG のインポート設定

現プロジェクトではSVG画像を使用しているのですが、CRAではインポート出来ていたものがViteではできなくなっていました。
そこでViteで使えるSVGパッケージを使用します。

npm install --save-dev vite-plugin-svgr

そして、vite.config.mtsに追記します。

...
import svgr from 'vite-plugin-svgr'

export default defineConfig({
  plugins: [react(), svgr()], // ここを追記
  server: {
...
  },
})

使い方ですが、以下のようにします。

import { ReactComponent as Mic } from '../../../assets/mic.svg'

↓

/// <reference types="vite-plugin-svgr/client" />
import Mic from '@/assets/mic.svg?react'

<Mic />

CRAでは、ReactComponentとしてインポートして使用していましたが、同じ記述方法では使用できなくなりました。

先ほどインストールしたvite-plugin-svgrの使い方としては、パスの最後に?reactとつけることにより、ReactComponentとしてインポートすることができます。

ただし、TypeScriptの場合は型推論が素直にできないので、/// <reference types="vite-plugin-svgr/client" />という宣言ヘルパーをファイル上部に記述することにより、無事にインポートすることができます。

8. require インポートの廃止

古いパッケージを使用している場合など、importではなくrequireでパッケージを使用しているときがあります。

vite.config.mtsのファイルの話にもありましたが、ViteはモジュールシステムをESMを使用しているため、require(CommonJSの書き方)は使用できません。

対処方法としては、TypeScriptの型定義ファイルが提供されていれば、それをインストールしたり、自身で使用する形を定義したりすればimportに置き換えられます。

例としては、以下の通り置き換えられます。

npm install @types/md5

const md5 = require('md5')

↓

import md5 from 'md5'

9. process.envアクセスの置き換え

envファイルなどの環境変数を読み込む場合、CRAではprocess.envで読み込むことが出来ますが、Viteではimport.meta.envを使用します。

const url = process.env.BASE_URL

↓

const url = import.meta.env.BASE_URL

ただやはりそのまますぐには使えません。

型定義ファイルをリネームする

/src/react-app-env.d.ts/src/vite-app-env.d.tsにリネームする。

さらに中身を変更します。

/// <reference types="react-scripts" />

↓

/// <reference types="vite/client" />

interface ImportMeta {
  readonly env: ImportMetaEnv
}

interface ImportMetaEnv {
  readonly VITE_BUILD_TARGET: string
  readonly VITE_VERSION: string
  readonly VITE_CONSOLE_LOG: boolean
}
  • ImportMeta: import.meta.envの型定義
  • ImportMetaEnv: import.meta.envの中身(envファイル)の型定義

上のようにすることで、envファイルで取り出した変数に形をつけることが出来ます。

envファイルの変数のリネーム

CRAではenvファイルの変数に対し、REACT_APP_<変数名>から始まる変数名にするというルールが有りました。

ViteではVite_<変数名>とする必要があるので、すべてリネームします。

REACT_APP_BUILD_TARGET=0
REACT_APP_VERSION=$npm_package_version
REACT_APP_CONSOLE_LOG=true

↓

VITE_BUILD_TARGET=0
VITE_VERSION=$npm_package_version
VITE_CONSOLE_LOG=true

これで無事に環境変数にアクセスすることができました。

10. Tailwind CSS 設定の修正

おまけに近いですが、Tailwind CSSを現プロジェクトでは使用しているため、こちらもVite対応を行いました。まさか、これも素直に使えなかったとは…。

まずはTailwind CSSをアンインストールします(!!)。というか再インストールし、initializeします。

npm uninstall tailwindcss postcss autoprefixer
npm install -D tailwindcss@3 postcss autoprefixer
npx tailwindcss init -p

こうすることにより、ある程度いい感じに使用できるようにしてくれました。

ちなみにバージョンを4系にすると、大幅に使い方が変わってしまい、苦戦したためバージョン3系を使用しています。

また、tailwind.config.jsのコンテントにindex.htmlを追加します。

content: ['./index.html', './src/**/*.{js,jsx,ts,tsx}'],

まとめ

CRAからViteへの移行手順を公開しました!
実はここまでに数日苦戦したので、苦しむ方が減ると幸いです。(遠い目)
また、最後に既存パッケージのアップデートを行うことを推奨しますが、何も考えずにアップデートを行うと痛い目に遭います!

私はたくさん痛い目に遭いました!!
それでは、よき開発ライフを!!



ギャップロを運営しているアップフロンティア株式会社では、一緒に働いてくれる仲間を随時、募集しています。 興味がある!一緒に働いてみたい!という方は下記よりご応募お待ちしております。
採用情報をみる