Viteのうれしい5つの機能

# はじめに

[Vite](https://ja.vitejs.dev/)とはJavaScriptアプリケーション開発ツールです。
ちなみにフランス語で「早い」という意味で `/vit/` と発音するそうです。

仕事の新規開発にあたって、はじめてViteを使いました。
その中でわかったうれしい機能を紹介しつつ、
行った設定や注意ポイントをメモとして書き残そうと思います。

# 環境

Vite v2.5.7（2021年9月現在で最新版）

# 今回の開発要求について

私の今回のViteプロジェクトでの開発要求はざっくり次の通りです。

- チャット形式で選択肢を選ぶことで回答を入力するフォームの開発
- 画面数は1ページ
- IE11をサポートしたい
- viteのビルドで生成するindex.htmlは使わない。HTMLはLaravel、JSはCDNが配信
- SPAのフレームワークが必要なほどの機能やインタラクションは求められなさそう

# サマリー

Viteは多くの機能があり、気持ちよく開発を開始できます。
今回の開発に限定して恩恵を受けたものを挙げると以下のようなものがあります。

- 日本語ドキュメントがある
- 設定なしでTypeScriptプロジェクトを開始できる
- 保存する度に高速かつ自動で更新内容を反映
- ビルド設定を細かく指定できる
- レガシーブラウザに対応する仕組みが内蔵
- 追加パッケージなしでenvの設定ができる

「5つの機能」というタイトルに反して項目が6つありますが
ドキュメントは機能に含まないで5つということでお願いします。
それぞれについて簡単にふれていきます。

## 日本語ドキュメントがある

ドキュメントの右上部から言語設定を切り替えることができます。
英語で読んだあとに日本語があることに気がつくやつを何度もやらかしてまして自戒を込めております。

## 設定なしでTypeScriptプロジェクトを開始できる

まずは雛形を作るのですが、npmとyarnのいずれにも対応しています。今回はyarnを使いました。
次のコマンドでプロジェクトを作成できます。

```
$ yarn create vite
...（一部省略）
Done. Now run:

  cd hoge
  yarn
  yarn dev
```

プロジェクト名、使用するフレームワーク、TypeScriptにするかどうかを、プロンプトで聞かれます。
これに答えるだけでプロジェクトが作成されます。
今回はフレームワークを使わずにTypeScriptで開発したかったので`vanilla-ts`を選択しました。


## 保存する度に高速かつ自動で更新内容を反映

ファイル保存を監視して内蔵されている開発サーバーがホットリロードしてくれます。
ファイルを数十に分けて合計約2,000行ほど記述したTypeScriptでの開発でしたが、
HMRとesbuildによって非常に高速でした。

### パスを指定してサーバーを起動

また、今回のプロジェクトではURLのパスを指定するため、baseオプションを付与しました。
package.jsonの `scripts.dev` ではviteコマンドが書かれていました。

```json:package.json
{
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "serve": "vite preview"
  }
}
```

devコマンドでパスを指定するように変えました。試しに同様のことをnpxで実行すると次のようになります。
（serveをつけなくても同じようです）

```
$ npx vite serve --base /anypath/
  vite v2.5.7 dev server running at:

  > Local: http://localhost:3000/anypath/
  > Network: use `--host` to expose

  ready in 238ms.
```

色々とオプションがあります。`vite --help`を眺めると面白いです。

## ビルド設定を細かく指定できる

ビルド設定は `vite.config.js` を編集して行います。
独自の設定とRollupの設定をここで書けます。

ビルドするとdist下にファイルが作成されます。
デフォルトではビルドごとにファイル名のサフィックスが変化します。

### manifest.jsonを使わずにRailsやLaravelからhtmlを配信する

RailsやLaravelからJSファイルを配信する場合はmanifest.jsonを使う方法が公式ドキュメントに記載してあります。

ややイレギュラーかもしれませんが、今回はリポジトリ構成の都合でmanifest.jsonを使用せずに、
LaravelのbladeテンプレートからCDNが配信するJSファイルを読み込みたいです。
そのため、この命名を一意にする必要がありました。
設定ファイルを次のようにしました。

```javascript:vite.config.js
export default {
  build: {
    rollupOptions: {
      output: {
        entryFileNames: 'entry.js',
        chunkFileNames: 'vendor.js',
      },
    },
  },
};
```

## レガシーブラウザに対応する仕組みが内蔵

今回の開発ではIE11に対応することにしました。
vite.config.jsでpolyfillの設定が簡単にできます。

```javascript:vite.config.js
import legacy from '@vitejs/plugin-legacy';

export default {
  plugins: [
    legacy({
      targets: ['ie >= 11'],
      additionalLegacyPolyfills: ['regenerator-runtime/runtime'],
    }),
  ],
};
```

tsconfig.jsonのtargetをes5にもしておきました。

### polyfillのファイル名は指定できない

ビルドで出力するpolyfillのファイル名はvite.config.jsの設定で固定できないようです。
GitHubに既にいくつかIssueがあがっていました。

## 追加パッケージなしでenvの設定ができる

環境変数は「.env.${mode}」ファイルに定義します。
アプリケーションへ公開する環境変数名はVITEから始まる必要があります。

```shell:.env.development
VITE_BACKEND_URL=//localhost
```

```shell:.env.production
VITE_BACKEND_URL=//example.com
```

次の表のように、実行するコマンドによってmodeは切り替わるようになっています。
また、独自のmode設定を作成することもできます。

| コマンド | mode |
|:-:|:-:|
| `npm run dev` | development |
| `npm run build` | production |
| `vite build --mode hoge` | hoge |

### envの余談その1: build:devを追加した

今回の開発ではローカル環境のLaravelアプリケーションで動作確認ができるように、
package.jsonにbuild:devコマンドを追加しました。
postbuildによってCDN代替サーバーへdist下を自動で移動させています。

```json:package.json
{
  "scripts": {
    "build:dev": "tsc && vite build --mode development",
    "postbuild:dev": "sh ./scripts/postBuild.sh"
  }
}
```

### envの余談その2: production判定

import.meta.env.PRODでproductionの判定を行えます。
今回の開発では本番環境のみでSentryのレポートをするために、組み込みの環境変数を使いました。

私の理解が怪しいところですが、import.meta.envの書き方に気をつけた方がよいかもしれません。
直接オブジェクトの中で評価できなかったため、一度変数として受けるようにしました。下記コードがその例です。
（※import.metaの仕様なのか、モジュールの設定なのか、viteのreplace処理なのか調べられていません。）

```typescript:tracking.ts
import * as Sentry from '@sentry/browser';
import { Integrations } from '@sentry/tracing';

const isProduction = import.meta.env.PROD;

export function loadTracker(): void {
  Sentry.init({
    ...(isProduction && {
      dsn: 'Sentryのエンドポイント',
    }),
    integrations: [new Integrations.BrowserTracing()],

    // Set tracesSampleRate to 1.0 to capture 100%
    // of transactions for performance monitoring.
    // We recommend adjusting this value in production
    tracesSampleRate: 1.0,
  });
}
```

# 終わりに

Viteを使うことで効率よく実装を進めることができました！