Viteで爆速なフロントエンド開発環境を作る
概要
Viteというビルドツールの紹介とReact + Typescript環境からの移行手順です。
導入したプロジェクトでは開発サーバの起動からページロードまで大体40秒くらいかかっていたものが、サーバは300ms程度で起動し(2回目以降)、ページロードまで大体2~3秒ぐらいに短縮されました。
非常に開発体験が良く、導入手順もそこまで複雑ではないため、技術スタックが合えば導入を検討しても良いかもしれません。
Viteとは?
ViteはVue.js作者のEvan You 氏が開発しているノーバンドルのビルドツールです。
Native ESModulesのimportを利用し、ブラウザから直接モジュールを読み込むことで高速な開発サーバを提供します。
プロダクションビルド時はRollupでバンドルし、静的アセットを出力します。
Vue.jsだけでなく、ReactやSvelteなどもサポートされています。
Viteはフランス語の「vite (速く)」を由来としており、「ヴィート」と読みます。
特徴
従来のバンドルツールとの違い
WebpackやRollupなど従来のバンドルツールは、アプリケーションの起動前にアプリケーション全体を走査し、依存解決しバンドルしたソースコードを出力していました。
(出典 : https://vitejs.dev/guide/why.html#slow-server-start)
これに対してViteは開発時は事前に依存解決と最低限のバンドル(pre-bundle)だけ行い、全体をバンドルすることなくESModulesのimportを介してソースコードを読み込むことで高速な開発サーバを提供します。
依存解決と事前バンドル (pre-bundle)
Viteはノーバンドルツールと言われますが、実際には事前バンドル(pre-bundle)というプロセスを挟みます。
これは以下の2つを目的として行われます。
① Native ESMへの変換
Viteは開発時は、全てのコードをNative ESM形式で提供することを前提としています。
そのためCommonJSやUMDで提供されている依存関係を事前にESMに変換する必要があります。
② パフォーマンス
一部のパッケージは多くの内部モジュールとの依存を抱えています。Viteはこのパッケージの依存関係を単一のモジュールに変換し、ページの読み込みパフォーマンスを向上させます。
例えば、lodash-es には600を超える内部モジュールとの依存があり、そのまま読み込むとブラウザは600以上のHTTPリクエストを発生させます。この大量のHTTPリクエストはHTTPの滞留を発生させ、ページの読み込みを著しく遅らせます。
この事前バンドルは、esbuild を利用して行われます。
esbuildは、Go製のバンドラーでありJSベースのバンドラーより高速にバンドルします。
また、事前バンドルの結果はnode_modules/.vite
以下にキャッシュされ、2回目以降は更に高速にサーバを起動します。
Production build
プロダクションのビルドは、Rollupを用いて行われます。
Rollupの設定と互換性があるため、Rollupのプラグインなどはそのまま使えます。
Typescript サポート
Viteは .ts
ファイルのインポートをサポートしていますが、ファイルのトランスパイルだけを行い、型のチェックは行いません。
型チェックは、IDEやビルド時に行われることを前提としています。
ブラウザサポート
Viteは、デフォルトではNative ESMをサポートするブラウザを対象とします。
対象ブラウザの変更は、設定で変更できます。
しかしこれは、構文変換のみを行うため、polyfillの読み込みなどは別途行う必要があります。
サーバサイドレンダリング (SSR)
サーバサイドレンダリング (SSR) のサポートはまだ実験段階のため注意が必要です。
React + Typescript環境からの移行
ここでは、React + Typescript 環境からの移行手順を記載します。
新しくReact + Typescript環境を用意する場合は、@vite/app からテンプレートを選択して行うことができます。
1. install
npm i vite @vitejs/plugin-react-refresh # yarn add vite @vitejs/plugin-react-refresh
2. configの追加
import reactRefresh from "@vitejs/plugin-react-refresh";
import { defineConfig } from "vite";
import path from 'path'
export default defineConfig({
plugins: [reactRefresh()],
resolve: { // optional
alias: [
{
find: '~/',
replacement: path.join(__dirname, 'src/'),
},
],
},
});
React製アプリのビルドとホットリロードのために、@vitejs/plugin-react-refresh
プラグインを用います。
pathエイリアスを設定する場合は、resolve以下に記載します。
3. エントリファイル(HTML)の作成
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>Vite App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
</body>
</html>
Viteはデフォルトでは、configと同ディレクトリにあるindex.html
をエントリポイントと見なします。
そこからモジュールタイプで読み込まれているJS (TS) ファイル、インラインスクリプト、あるいはCSSも参照し依存を解決します。
4. npm-scriptsの追加
package.json
"scripts": {
"start": "vite",
"build": "tsc --noEmit && vite build",
},
Viteは前述の通り、Typescriptのトランスパイルは行いますが、型チェックは行わないため、ビルド時に型チェックを行うことが推奨されます。
また、Git フック(pre-commitやpre-pushなど) でも型チェックを走らせておくのが良いかと思います。
5. 起動
npm run start # yarn start
devtoolなどを見ると、エントリポイントから読み込まれたファイルを起点として、node_modules/.vite
以下に事前バンドルされたファイルを読み込んでいるのが分かると思います。
最低限、必要な手順は上記だけです。
注意事項
新しいJSX トランスフォームへの対応
ViteはJSXを利用しているファイルでReactをインポートしていないと以下のようなエラーが出ます。
Uncaught ReferenceError: React is not defined
React 17以前では、JSXを利用するには明示的にReactをインポートする必要がありました。
これは、React 17 + Typescript 4.1以降、tsconfigのcompilerOptions.jsx
に react-jsx
を指定することで自動で解決されるようになりました
Viteはesbuildを用いてTypescriptを変換しますが、esbuildはまだこの変換に対応していないため、JSXを利用していない場合は明示的にReactをインポートする必要があります。
もしくは、configのesbuild.jsxEnject
にインポート文を記載することで全tsxファイルにインポート文を注入しますが、既にインポートしているファイルにも注入してしまうため注意が必要です。
vite.config.ts
export default defineConfig({
esbuild: {
jsxInject: "import React from 'react';",
},
});
環境変数の設定
Viteは、rootディレクトリ内の.env
ファイルを参照して、環境変数をimport.meta.env
オブジェクトにロードします。
.env # loaded in all cases
.env.local # loaded in all cases, ignored by git
.env.[mode] # only loaded in specified mode
.env.[mode].local # only loaded in specified mode, ignored by git
意図しない環境変数の漏洩を防ぐため、VITE_
の接頭辞がついた変数のみ展開します。
DB_PASSWORD=foobar #展開されない
VITE_SOME_KEY=123 #展開される
その他
その他の説明や設定についてはDocumentに記載されています。
また、作者の解説動画がYoutubeに上がっているので時間がある方は見てみると良いかもしれません。
Discussion