base-vite

star 0

Vite(8+)の設定・プラグイン・HMR・環境変数・プロキシ・SSR・ライブラリモード・ 依存関係の事前バンドル・ビルド最適化を扱う時に使用する。 vite.config.ts や Vite プラグイン、Vite ベースのプロジェクトの作業時に有効化する。

Ag-2O By Ag-2O schedule Updated 6/15/2026

name: base-vite description: >- Vite(8+)の設定・プラグイン・HMR・環境変数・プロキシ・SSR・ライブラリモード・ 依存関係の事前バンドル・ビルド最適化を扱う時に使用する。 vite.config.ts や Vite プラグイン、Vite ベースのプロジェクトの作業時に有効化する。 user-invocable: false

Vite パターン

Vite 8+ プロジェクトのビルドツールと開発サーバーのパターン集。設定・環境変数・プロキシ・ ライブラリモード・依存関係の事前バンドル・プロダクションでよくある落とし穴を扱う。

適用タイミング

  • vite.config.ts / vite.config.js の設定
  • 環境変数や .env ファイルのセットアップ
  • API バックエンドへの開発サーバープロキシの設定
  • ビルド出力の最適化(チャンク・ミニファイ・アセット)
  • build.lib によるライブラリの公開
  • 依存関係の事前バンドルや CJS/ESM 相互運用のトラブルシューティング
  • HMR・開発サーバー・ビルドエラーのデバッグ
  • Vite プラグインの選定や適用順序の決定

仕組み

  • 開発モードはソースファイルをネイティブ ESM としてそのまま配信する — バンドルしない。 変換はモジュールリクエストごとにオンデマンドで行われるため、コールドスタートが速く HMR が正確。
  • ビルドモードは Rolldown(v7+)または Rollup(v5–v6)でアプリをプロダクション用にバンドルする。 ツリーシェイキング・コード分割・Oxc ベースのミニファイを行う。
  • 依存関係の事前バンドルは esbuild で CJS/UMD 依存を一度だけ ESM へ変換し、結果を node_modules/.vite にキャッシュする。以降の起動ではこの処理をスキップする。
  • プラグインは開発とビルドで統一されたインターフェースを共有する — 同じプラグインオブジェクトが、 開発サーバーのオンデマンド変換とプロダクションパイプラインの両方で動く。
  • 環境変数はビルド時に静的にインライン化される。VITE_ プレフィックス付きの変数はバンドル内の 公開定数になり、プレフィックスなしの変数はクライアントコードから一切見えない。

設定

基本の設定

// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: { '@': new URL('./src', import.meta.url).pathname },
  },
})

条件付きの設定

// vite.config.ts
import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig(({ command, mode }) => {
  const env = loadEnv(mode, process.cwd())   // VITE_ プレフィックスのみ(安全)

  return {
    plugins: [react()],
    server: command === 'serve' ? { port: 3000 } : undefined,
    define: {
      __API_URL__: JSON.stringify(env.VITE_API_URL),
    },
  }
})

主要な設定オプション

キー デフォルト 説明
root '.' プロジェクトルート(index.html の場所)
base '/' デプロイ先アセットの公開ベースパス
envPrefix 'VITE_' クライアントへ公開する環境変数のプレフィックス
build.outDir 'dist' 出力ディレクトリ
build.minify 'oxc' ミニファイア('oxc''terser'false
build.sourcemap false true'inline''hidden'

プラグイン

定番プラグイン

プラグインのニーズの大半は、メンテナンスの行き届いた少数のパッケージでカバーできる。 自作する前にまずこれらを検討する。

プラグイン 目的 使うケース
@vitejs/plugin-react-swc SWC による React HMR + Fast Refresh React アプリのデフォルト(Babel 版より高速)
@vitejs/plugin-react Babel による React HMR + Fast Refresh Babel プラグイン(emotion・MobX デコレーター)が必要な場合のみ
@vitejs/plugin-vue Vue 3 SFC サポート Vue アプリ
vite-plugin-checker tsc + ESLint をワーカースレッドで実行し HMR オーバーレイ表示 すべての TypeScript アプリvite build は型チェックしない
vite-tsconfig-paths tsconfig.jsonpaths エイリアスを反映 tsconfig.json に既にエイリアスがある場合
vite-plugin-dts ライブラリモードで .d.ts を出力 TypeScript ライブラリの公開時
vite-plugin-svgr SVG を React コンポーネントとしてインポート SVG をコンポーネントとして使う React アプリ
rollup-plugin-visualizer バンドルのツリーマップ / サンバーストレポート 定期的なバンドルサイズ監査(enforce: 'post' を使う)
vite-plugin-pwa ゼロコンフィグ PWA + Workbox オフライン対応アプリ

重要: vite build はトランスパイルのみで型チェックをしないvite-plugin-checker を追加するか CI で tsc --noEmit を実行しない限り、型エラーは静かにプロダクションへ出荷される。

カスタムプラグインの作成

自作が必要になることはまれ — 大半のニーズは既存プラグインでカバーできる。必要な場合は vite.config.ts 内のインラインから始め、再利用するときだけ切り出す。

// vite.config.ts — 最小のインラインプラグイン
function myPlugin(): Plugin {
  return {
    name: 'my-plugin',                       // 必須。一意にする
    enforce: 'pre',                           // 'pre' | 'post'(任意)
    apply: 'build',                           // 'build' | 'serve'(任意)
    transform(code, id) {
      if (!id.endsWith('.custom')) return
      return { code: transformCustom(code), map: null }
    },
  }
}

主要なフック: transform(ソースの変換)、resolveId + load(仮想モジュール)、 transformIndexHtml(HTML への注入)、configureServer(開発用ミドルウェアの追加)、 hotUpdate(カスタム HMR — v7+ で非推奨の handleHotUpdate を置き換え)。

仮想モジュール\0 プレフィックスの慣習を使う — resolveId'\0virtual:my-id' を返すことで 他のプラグインが処理をスキップする。ユーザーコードは 'virtual:my-id' をインポートする。

プラグイン API の全容は vite.dev/guide/api-plugin を参照する。 変換パイプラインのデバッグには開発中に vite-plugin-inspect を使う。

HMR API

フレームワークプラグイン(@vitejs/plugin-react@vitejs/plugin-vue 等)が HMR を自動処理する。 import.meta.hot を直接使うのは、更新をまたいで状態を保持する必要があるカスタム状態ストア・ 開発ツール・フレームワーク非依存のユーティリティを作る場合のみとする。

// src/store.ts — バニラモジュールの手動 HMR
if (import.meta.hot) {
  // 更新をまたいで状態を保持する(.data は再代入せず、必ずプロパティを変更する)
  import.meta.hot.data.count = import.meta.hot.data.count ?? 0

  // モジュールが置き換えられる前に副作用をクリーンアップする
  import.meta.hot.dispose((data) => clearInterval(data.intervalId))

  // 自身のモジュールの更新を受け入れる
  import.meta.hot.accept()
}

import.meta.hot のコードはプロダクションビルドからツリーシェイキングで除去される — ガードを手で外す必要はない。

環境変数

Vite は .env.env.local.env.[mode].env.[mode].local の順に読み込む (後のものが前を上書きする)。*.local ファイルは gitignore され、ローカルのシークレット用。

クライアントサイドからのアクセス

クライアントコードへ公開されるのは VITE_ プレフィックス付きの変数のみ:

import.meta.env.VITE_API_URL   // string
import.meta.env.MODE            // 'development' | 'production' | カスタム
import.meta.env.BASE_URL        // base 設定の値
import.meta.env.DEV             // boolean
import.meta.env.PROD            // boolean
import.meta.env.SSR             // boolean

設定ファイル内での環境変数の利用

// vite.config.ts
import { defineConfig, loadEnv } from 'vite'

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd())          // VITE_ プレフィックスのみ(安全)
  return {
    define: {
      __API_URL__: JSON.stringify(env.VITE_API_URL),
    },
  }
})

セキュリティ

VITE_ プレフィックスはセキュリティ境界ではない

VITE_ プレフィックス付きの変数はビルド時にクライアントバンドルへ静的にインライン化される。 ミニファイ・base64 エンコード・ソースマップの無効化では隠せない。攻撃者は配信された JavaScript から あらゆる VITE_ 変数を抽出できる。

ルール: VITE_ 変数に入れてよいのは公開値(API URL・フィーチャーフラグ・公開鍵)のみ。 シークレット(API トークン・データベース URL・秘密鍵)は API やサーバーレス関数の背後の サーバーサイドに置く。

loadEnv('') の罠

// Bad: 第 3 引数に '' を渡すと、サーバーのシークレットを含む全環境変数が読み込まれ、
// define 経由でクライアントコードへインライン化できる状態になる
const env = loadEnv(mode, process.cwd(), '')

// Good: プレフィックスを明示する
const env = loadEnv(mode, process.cwd(), ['VITE_', 'APP_'])

プロダクションのソースマップ

プロダクションのソースマップは元のソースコードを漏らす。エラートラッカー(Sentry・Bugsnag)へ アップロードしてローカルから削除する運用でない限り、無効のままにする。

build: {
  sourcemap: false,                                  // デフォルト — このままにする
}

.gitignore チェックリスト

  • .env.local.env.*.local — ローカルのシークレット上書き
  • dist/ — ビルド出力
  • node_modules/.vite — 事前バンドルキャッシュ(古いエントリが幻のエラーを引き起こす)

サーバープロキシ

// vite.config.ts — server.proxy
server: {
  proxy: {
    '/foo': 'http://localhost:4567',                    // 文字列の短縮形

    '/api': {
      target: 'http://localhost:8080',
      changeOrigin: true,                               // バーチャルホストのバックエンドに必要
      rewrite: (path) => path.replace(/^\/api/, ''),
    },
  },
}

WebSocket をプロキシする場合はルート設定に ws: true を追加する。

ビルド最適化

手動チャンク

// vite.config.ts — build.rolldownOptions
build: {
  rolldownOptions: {
    output: {
      // オブジェクト形式: 特定のパッケージをグループ化する
      manualChunks: {
        'react-vendor': ['react', 'react-dom'],
        'ui-vendor': ['@radix-ui/react-dialog', '@radix-ui/react-popover'],
      },
    },
  },
}
// 関数形式: ヒューリスティックで分割する
manualChunks(id) {
  if (id.includes('node_modules/react')) return 'react-vendor'
  if (id.includes('node_modules')) return 'vendor'
}

パフォーマンス

バレルファイルを避ける

バレルファイル(ディレクトリ全体を再エクスポートする index.ts)は、1 つのシンボルを インポートしただけでも、再エクスポートされた全ファイルの読み込みを Vite に強制する。 公式ドキュメントが挙げる開発サーバー低速化の最大要因。

// Bad — ユーティリティ 1 つのインポートでバレル全体が読み込まれる
import { slash } from '@/utils'

// Good — 直接インポートなら対象の 1 ファイルだけが読み込まれる
import { slash } from '@/utils/slash'

インポートの拡張子を明示する

暗黙の拡張子 1 つにつき、resolve.extensions 経由で最大 6 回のファイルシステムチェックが発生する。 大規模なコードベースでは積み重なる。

// Bad
import Component from './Component'

// Good
import Component from './Component.tsx'

tsconfig.jsonallowImportingTsExtensionsresolve.extensions を、実際に使う拡張子だけに絞る。

ホットパスのルートをウォームアップする

server.warmup.clientFiles は、ブラウザがリクエストする前に既知のホットエントリを事前変換する — 大規模アプリのコールドロード時のリクエストウォーターフォールを解消する。

// vite.config.ts
server: {
  warmup: {
    clientFiles: ['./src/main.tsx', './src/routes/**/*.tsx'],
  },
}

遅い開発サーバーのプロファイリング

vite dev が遅いと感じたら、まず vite --profile で起動し、アプリを操作してから p+enter.cpuprofile を保存する。Speedscope に読み込み、どのプラグインが 時間を食っているかを特定する — 多くの場合、コミュニティプラグインの buildStartconfigconfigResolved フックが原因。

ライブラリモード

npm パッケージを公開する場合は build.lib を使う。設定の詳細よりも重要な footgun が 2 つある。

  1. 型は出力されないvite-plugin-dts を追加するか、別途 tsc --emitDeclarationOnly を実行する。
  2. peer dependencies は必ず external 化する — 指定漏れの peer はライブラリへバンドルされ、 利用側でランタイム二重化エラーを引き起こす。
// vite.config.ts
build: {
  lib: {
    entry: 'src/index.ts',
    formats: ['es', 'cjs'],
    fileName: (format) => `my-lib.${format}.js`,
  },
  rolldownOptions: {
    external: ['react', 'react-dom', 'react/jsx-runtime'],  // すべての peer dep
  },
}

SSR の externals

素の createServer({ middlewareMode: true }) セットアップはフレームワーク作者の領分。 ほとんどのアプリは Nuxt・Remix・SvelteKit・Astro・TanStack Start を使うべき。 フレームワーク利用者として調整するのは、依存が SSR で壊れたときの externals 設定:

// vite.config.ts — ssr オプション
ssr: {
  external: ['node-native-package'],           // SSR バンドル内で require() のまま残す
  noExternal: ['esm-only-package'],            // SSR 出力へ強制バンドル(SSR エラーの大半を解決)
  target: 'node',                              // 'node' または 'webworker'
}

依存関係の事前バンドル

Vite は CJS/UMD を ESM へ変換しリクエスト数を減らすために、依存関係を事前バンドルする。

// vite.config.ts — optimizeDeps
optimizeDeps: {
  include: [
    'lodash-es',                              // 既知の重い依存を強制的に事前バンドルする
    'cjs-package',                            // 相互運用の問題を起こす CJS 依存
    'deep-lib/components/**',                 // 深いインポートには glob を使う
  ],
  exclude: ['local-esm-package'],             // exclude する場合は有効な ESM であること
  force: true,                                // キャッシュを無視して再最適化(一時的なデバッグ用)
}

よくある落とし穴

開発とビルドの挙動が一致しない

開発時は esbuild/Rolldown による変換、ビルド時は Rolldown によるバンドルを使う。 CJS ライブラリは両者で挙動が異なることがある。デプロイ前に必ず vite build && vite preview で検証する。

デプロイ後の古いチャンク

新しいビルドは新しいチャンクハッシュを生成する。セッション継続中のユーザーが、 もう存在しない古いファイル名をリクエストする。Vite に組み込みの解決策はない。緩和策:

  • デプロイ後も一定期間、古い dist/assets/ のファイルを配信し続ける
  • ルーターで動的インポートのエラーを捕捉し、ページを強制リロードする

Docker とコンテナ

Vite はデフォルトで localhost にバインドするため、コンテナの外から到達できない:

// vite.config.ts — Docker / コンテナ向け設定
server: {
  host: true,                                  // 0.0.0.0 にバインドする
  hmr: { clientPort: 3000 },                   // リバースプロキシの背後にいる場合
}

モノレポでのファイルアクセス

Vite はファイル配信をプロジェクトルート内に制限する。ルート外のパッケージはブロックされる:

// vite.config.ts — モノレポでのファイルアクセス
server: {
  fs: {
    allow: ['..'],                             // 親ディレクトリ(ワークスペースルート)を許可する
  },
}

アンチパターン

// Bad: envPrefix を '' にすると、シークレットを含む全環境変数がクライアントへ公開される
envPrefix: ''

// Bad: アプリのソースコードで require() が動く前提 — Vite は ESM ファースト
const lib = require('some-lib')                // import を使う

// Bad: node_modules を全部パッケージごとのチャンクに分割する — 数百の極小ファイルができる
manualChunks(id) {
  if (id.includes('node_modules')) {
    return id.split('node_modules/')[1].split('/')[0]   // パッケージごとに 1 チャンク
  }
}

// Bad: ライブラリモードで peer dep を external 化しない — ランタイム二重化エラーになる
// rolldownOptions.external なしの build.lib

// Bad: 非推奨の esbuild ミニファイアを使う
build: { minify: 'esbuild' }                  // 'oxc'(デフォルト)か 'terser' を使う

// Bad: import.meta.hot.data を再代入で書き換える
import.meta.hot.data = { count: 0 }           // 誤り: 再代入ではなくプロパティを変更する
import.meta.hot.data.count = 0                 // 正しい

プロセス面のアンチパターン:

  • vite preview をプロダクションサーバーとして使う — ビルド済みバンドルのスモークテスト用。 dist/ は本物の静的ホスト(NGINX・Cloudflare Pages・Vercel static)へデプロイするか、 マルチステージ Dockerfile を使う。
  • vite build が型チェックすると期待する — トランスパイルのみ。型エラーは静かに プロダクションへ出荷される。vite-plugin-checker を追加するか CI で tsc --noEmit を実行する。
  • デフォルトで @vitejs/plugin-legacy を入れる — バンドルが約 40% 肥大化し、ソースマップ系の バンドルアナライザーが壊れる。モダンブラウザの 95%+ のユーザーには不要。 推測ではなく実際のアナリティクスに基づいて判断する。
  • tsconfig.json の paths と重複する resolve.alias を 30 個以上手書きするvite-tsconfig-paths を使う。Excalidraw や PostHog で観測された事例で、新規プロジェクトでは避ける。
  • 依存変更後に古い node_modules/.vite を残す — 事前バンドルキャッシュが幻のエラーを起こす。 ブランチ切り替え時や依存へのパッチ適用後にクリアする。

クイックリファレンス

パターン 使うケース
defineConfig 常に使う — 型推論が効く
loadEnv(mode, root, ['VITE_']) 設定ファイル内での環境変数アクセス(プレフィックスを明示)
vite-plugin-checker すべての TypeScript アプリ(型チェックの穴を埋める)
vite-tsconfig-paths 手書きの resolve.alias の代わりに使う
optimizeDeps.include 相互運用の問題を起こす CJS 依存
server.proxy 開発時に API リクエストをバックエンドへルーティングする
server.host: true Docker・コンテナ・リモートアクセス
server.warmup.clientFiles ホットパスのルートを事前変換する
build.lib + external npm パッケージの公開
manualChunks(オブジェクト形式) ベンダーバンドルの分割
vite --profile 遅い開発サーバーのデバッグ
vite build && vite preview プロダクションバンドルのローカル検証(プロダクションサーバーではない)

関連

  • スキル: base-react — React のパターン、 base-docker — コンテナ化された開発環境
Install via CLI
npx skills add https://github.com/Ag-2O/claude-code-small --skill base-vite
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator