🤖

Viteで爆速なフロントエンド開発環境を作る

2021/06/19に公開

概要

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のプラグインなどはそのまま使えます。

https://vitejs.dev/guide/build.html#customizing-the-build

Typescript サポート

Viteは .ts ファイルのインポートをサポートしていますが、ファイルのトランスパイルだけを行い、型のチェックは行いません。
型チェックは、IDEやビルド時に行われることを前提としています。

https://vitejs.dev/guide/features.html#typescript

ブラウザサポート

Viteは、デフォルトではNative ESMをサポートするブラウザを対象とします。
対象ブラウザの変更は、設定で変更できます。
しかしこれは、構文変換のみを行うため、polyfillの読み込みなどは別途行う必要があります。

https://vitejs.dev/guide/build.html#building-for-production

サーバサイドレンダリング (SSR)

サーバサイドレンダリング (SSR) のサポートはまだ実験段階のため注意が必要です。

https://vitejs.dev/guide/ssr.html#server-side-rendering

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.jsxreact-jsx を指定することで自動で解決されるようになりました

https://ja.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html

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 #展開される

https://vitejs.dev/guide/env-and-mode.html#env-files

その他

その他の説明や設定についてはDocumentに記載されています。

https://vitejs.dev/guide/

また、作者の解説動画がYoutubeに上がっているので時間がある方は見てみると良いかもしれません。

https://youtu.be/xXrhg26VCSc

Discussion