Vite 6完全実践ガイド〜開発サーバー・Rolldown・プラグイン・Webpack移行【2026年版】〜

Webpack の遅さに耐えかねて Vite を試したものの、設定の全体像がつかめず途中で挫折した経験はないでしょうか。本記事では Vite 6.x(2026年版・Rolldown 切替期)を前提に、インストールから本番デプロイまで 実コード 40+ ブロック で順を追って解説します。コピペでそのまま動く JS / TS 設定を中心に、vite.config.ts・プラグイン・HMR・SSR・SSG・Webpack 移行までを一気通貫で押さえます。
本稿の対象は「Vite を本気で実務投入したい人」「Webpack から逃げ切りたい人」「Rolldown / Lightning CSS など最新動向を 1 本で把握したい人」です。読み終える頃には vite.config.ts を自分の手で書ける 状態になっているはずです。

  1. 1. Vite とは何か(esbuild × Rollup × Rolldown の三層構造)
    1. 1.1 動作要件
  2. 2. インストールとプロジェクト初期化
    1. 2.1 npm create vite で雛形を作る
    2. 2.2 公式テンプレート一覧
    3. 2.3 プロジェクト構造
    4. 2.4 package.json の scripts
  3. 3. vite.config.ts の基本
    1. 3.1 最小構成
    2. 3.2 mode / command で分岐する関数形式
    3. 3.3 server 設定(port / host / proxy / cors)
    4. 3.4 build 設定(ターゲット・出力・チャンク分割)
  4. 4. 環境変数(import.meta.env)と mode
    1. 4.1 .env ファイルの優先順位
    2. 4.2 VITE_ プレフィックスで自動公開
    3. 4.3 TypeScript で型補完を効かせる
    4. 4.4 利用例
  5. 5. プラグインシステム
    1. 5.1 公式プラグイン
    2. 5.2 React + SWC 構成例
    3. 5.3 自作プラグインの最小骨格
    4. 5.4 自作プラグインを config から読み込む
    5. 5.5 プラグインのフック順
  6. 6. CSS と各種アセット
    1. 6.1 PostCSS + Tailwind 連携(Tailwind v4)
    2. 6.2 SCSS / Less / Stylus(プリプロセッサは自動検出)
    3. 6.3 CSS Modules
    4. 6.4 Lightning CSS(高速ベンダープレフィックス + Minify)
  7. 7. アセット import(画像 / SVG / JSON / WASM / Worker)
    1. 7.1 画像 import
    2. 7.2 SVG をコンポーネントとして使う
    3. 7.3 JSON import
    4. 7.4 WASM import
    5. 7.5 Web Worker import
    6. 7.6 Shared Worker
  8. 8. import.meta.glob と dynamic import
    1. 8.1 import.meta.glob(複数ファイル一括 import)
    2. 8.2 ルーティングへの応用
    3. 8.3 dynamic import(コード分割)
  9. 9. HMR(Hot Module Replacement)
    1. 9.1 HMR は default で有効
    2. 9.2 import.meta.hot API でカスタム HMR
    3. 9.3 dispose で副作用クリーンアップ
    4. 9.4 HMR を完全無効化
  10. 10. base path / 静的ホスティング
    1. 10.1 サブパス配信(GitHub Pages 等)
    2. 10.2 環境ごとに base を切り替える
    3. 10.3 ビルド成果物の確認
  11. 11. Rolldown(2026 年の新ビルダ)
    1. 11.1 rolldown-vite を試す
    2. 11.2 Rollup プラグイン互換
  12. 12. SSR(サーバサイドレンダリング)
    1. 12.1 SSR エントリの分割
    2. 12.2 Express から Vite middleware を呼ぶ
    3. 12.3 ssr.external / noExternal
  13. 13. SSG(静的サイト生成)
    1. 13.1 vite-ssg(Vue 系)
    2. 13.2 React なら vite-plugin-ssr-react / react-router の prerender
  14. 14. Vitest 連携(同じ vite.config をテストにも使う)
  15. 15. 主要ホスティングへのデプロイ
    1. 15.1 Vercel
    2. 15.2 Netlify
    3. 15.3 Cloudflare Pages
    4. 15.4 GitHub Pages
    5. 15.5 さくら / レンタルサーバの静的ホスティング
  16. 16. Webpack からの移行
    1. 16.1 webpack.config.js → vite.config.ts 対応表
    2. 16.2 process.env を import.meta.env に置換
    3. 16.3 require → import に置換
    4. 16.4 CommonJS 依存への対処
    5. 16.5 移行手順サマリ
  17. 17. パフォーマンスチューニング
    1. 17.1 optimizeDeps で事前バンドル制御
    2. 17.2 build.rollupOptions.output.manualChunks で分割
    3. 17.3 brotli / gzip 圧縮プラグイン
    4. 17.4 バンドルサイズ可視化
  18. 18. esbuild との関係
  19. 19. アンチパターンと落とし穴
    1. 19.1 NG: dev サーバが遅いのに optimizeDeps を放置
    2. 19.2 NG: 巨大ライブラリの全体 import
    3. 19.3 NG: process.env を本番で参照
    4. 19.4 NG: public/ に大量の動的アセット
    5. 19.5 NG: SSR で window を直接参照
  20. 20. まとめ・次に学ぶこと

1. Vite とは何か(esbuild × Rollup × Rolldown の三層構造)

Vite(ヴィート、フランス語で「速い」)は Evan You(Vue.js 作者)が 2020 年に発表した次世代フロントエンドビルドツールです。Vite 6.x 時点では以下の三層構造になっています。

  • 開発時: esbuild で依存関係を事前バンドル + ブラウザネイティブ ES Modules で配信
  • 本番ビルド: Rollup(将来的に Rolldown へ段階移行)
  • CSS: PostCSS + Lightning CSS(オプトイン)

Webpack が「全てを JS で再実装したバンドラ」なのに対し、Vite は「ブラウザのネイティブ ESM を活かす dev サーバ + 高速ネイティブツールチェーン」という設計です。1000 ファイル超のプロジェクトでも初回起動が 1 秒前後で済むのはこの設計のおかげです。

1.1 動作要件

# Node.js 18.0+ / 20.19+ / 22.12+ が必須(Vite 6.x)
node -v
# v22.14.0

# パッケージマネージャ(npm / pnpm / yarn / bun いずれでも可)
npm -v
pnpm -v

2. インストールとプロジェクト初期化

2.1 npm create vite で雛形を作る

# 対話形式
npm create vite@latest my-app

# 非対話(テンプレートと言語を直接指定)
npm create vite@latest my-app -- --template react-ts

# pnpm / yarn / bun の場合
pnpm create vite my-app --template vue-ts
yarn create vite my-app --template svelte-ts
bun create vite my-app --template vanilla-ts

2.2 公式テンプレート一覧

# 主要テンプレート(2026年5月時点)
vanilla       vanilla-ts
react         react-ts        react-swc      react-swc-ts
vue           vue-ts
preact        preact-ts
lit           lit-ts
svelte        svelte-ts
solid         solid-ts
qwik          qwik-ts

2.3 プロジェクト構造

my-app/
├── index.html          # エントリ HTML(ルート直下が Vite 流儀)
├── package.json
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts      # ここに全設定を書く
├── public/             # そのまま配信される静的ファイル
│   └── favicon.svg
└── src/
    ├── main.ts         # エントリ JS/TS
    ├── App.tsx
    ├── assets/
    └── components/

2.4 package.json の scripts

{
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "preview": "vite preview --port 4173",
    "lint": "eslint . --ext .ts,.tsx",
    "test": "vitest"
  }
}

3. vite.config.ts の基本

3.1 最小構成

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

export default defineConfig({
  plugins: [react()],
})

3.2 mode / command で分岐する関数形式

// 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(), '')
  return {
    plugins: [react()],
    define: {
      __APP_VERSION__: JSON.stringify(env.npm_package_version),
    },
    build: {
      sourcemap: command === 'serve' ? 'inline' : false,
    },
  }
})

3.3 server 設定(port / host / proxy / cors)

// vite.config.ts
export default defineConfig({
  server: {
    host: '0.0.0.0',          // LAN 公開(スマホ実機確認に必須)
    port: 5173,
    strictPort: true,         // 5173 が空いてなければ起動失敗
    open: '/dashboard',       // 起動時にブラウザを開くパス
    cors: true,
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^/api/, ''),
      },
      '/ws': {
        target: 'ws://localhost:8081',
        ws: true,
      },
    },
  },
})

3.4 build 設定(ターゲット・出力・チャンク分割)

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

export default defineConfig({
  build: {
    target: 'es2022',
    outDir: 'dist',
    assetsDir: 'assets',
    cssCodeSplit: true,
    sourcemap: true,
    minify: 'esbuild',          // 'terser' も指定可
    chunkSizeWarningLimit: 1000,
    rollupOptions: {
      output: {
        manualChunks: {
          react: ['react', 'react-dom'],
          ui: ['@radix-ui/react-dialog', '@radix-ui/react-tooltip'],
        },
      },
    },
  },
})

4. 環境変数(import.meta.env)と mode

4.1 .env ファイルの優先順位

# 全 mode 共通
.env

# 全 mode のローカル(.gitignore 推奨)
.env.local

# 特定 mode のみ
.env.development
.env.production
.env.staging         # vite build --mode staging で読まれる

# ローカル特定 mode
.env.production.local

4.2 VITE_ プレフィックスで自動公開

# .env
VITE_API_BASE=https://api.example.com
VITE_GA_ID=G-XXXXXXXXXX

# プレフィックスなしはクライアントに露出しない
DATABASE_URL=postgres://...    # build 時にも参照不可

4.3 TypeScript で型補完を効かせる

// src/vite-env.d.ts
/// <reference types="vite/client" />

interface ImportMetaEnv {
  readonly VITE_API_BASE: string
  readonly VITE_GA_ID: string
}

interface ImportMeta {
  readonly env: ImportMetaEnv
}

4.4 利用例

// src/api/client.ts
const BASE = import.meta.env.VITE_API_BASE

export async function fetchUser(id: string) {
  const res = await fetch(`${BASE}/users/${id}`)
  if (!res.ok) throw new Error(`HTTP ${res.status}`)
  return res.json()
}

// 開発/本番の切り分け
if (import.meta.env.DEV) {
  console.log('[dev] base =', BASE)
}

5. プラグインシステム

5.1 公式プラグイン

npm i -D @vitejs/plugin-react        # React(Babel ベース)
npm i -D @vitejs/plugin-react-swc    # React(SWC・10倍速い)
npm i -D @vitejs/plugin-vue
npm i -D @vitejs/plugin-vue-jsx
npm i -D @sveltejs/vite-plugin-svelte
npm i -D vite-plugin-solid
npm i -D @preact/preset-vite

5.2 React + SWC 構成例

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

export default defineConfig({
  plugins: [
    react({
      // SWC オプション(emotion / styled-components 等)
      plugins: [
        ['@swc/plugin-emotion', {}],
      ],
    }),
  ],
})

5.3 自作プラグインの最小骨格

// plugins/banner.ts
import type { Plugin } from 'vite'

export default function banner(text: string): Plugin {
  return {
    name: 'vite-plugin-banner',
    enforce: 'post',
    generateBundle(_options, bundle) {
      for (const file of Object.values(bundle)) {
        if (file.type === 'chunk' && file.fileName.endsWith('.js')) {
          file.code = `/*! ${text} */n` + file.code
        }
      }
    },
  }
}

5.4 自作プラグインを config から読み込む

// vite.config.ts
import banner from './plugins/banner'

export default defineConfig({
  plugins: [
    banner(`Built at ${new Date().toISOString()}`),
  ],
})

5.5 プラグインのフック順

// 主要フック(Rollup 互換 + Vite 固有)
const plugin: Plugin = {
  name: 'demo',
  config(userConfig, env) {},        // config 解決前
  configResolved(resolved) {},       // config 解決後
  configureServer(server) {},        // dev サーバ起動時
  transformIndexHtml(html) {},       // index.html の変換
  resolveId(source) {},
  load(id) {},
  transform(code, id) {},
  handleHotUpdate(ctx) {},           // HMR カスタム
}

6. CSS と各種アセット

6.1 PostCSS + Tailwind 連携(Tailwind v4)

npm i -D tailwindcss @tailwindcss/vite
// vite.config.ts
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
  plugins: [tailwindcss()],
})
/* src/index.css */
@import "tailwindcss";

@theme {
  --color-brand-500: oklch(0.72 0.17 250);
}

6.2 SCSS / Less / Stylus(プリプロセッサは自動検出)

npm i -D sass        # .scss .sass
npm i -D less        # .less
npm i -D stylus      # .styl .stylus
// vite.config.ts
export default defineConfig({
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@use "@/styles/variables" as *;`,
      },
    },
  },
})

6.3 CSS Modules

// Button.module.css → 自動で Module 化
// vite.config.ts
export default defineConfig({
  css: {
    modules: {
      localsConvention: 'camelCaseOnly',
      generateScopedName: '[name]__[local]__[hash:base64:5]',
    },
  },
})
// Button.tsx
import styles from './Button.module.css'

export const Button = () => (
  <button className={styles.primary}>Click</button>
)

6.4 Lightning CSS(高速ベンダープレフィックス + Minify)

// vite.config.ts
export default defineConfig({
  css: {
    transformer: 'lightningcss',
    lightningcss: {
      targets: {
        chrome: 100 << 16,
        firefox: 100 << 16,
        safari: 15 << 16,
      },
    },
  },
  build: {
    cssMinify: 'lightningcss',
  },
})

7. アセット import(画像 / SVG / JSON / WASM / Worker)

7.1 画像 import

// 通常 import → URL 文字列が返る
import logoUrl from './assets/logo.png'
console.log(logoUrl) // /assets/logo-a1b2c3.png

// クエリで Base64 インライン化
import inlineLogo from './assets/logo.png?inline'

// ?url で明示的に URL を取得
import url from './assets/logo.png?url'

// ?raw でテキスト取得
import shader from './shader.glsl?raw'

7.2 SVG をコンポーネントとして使う

npm i -D vite-plugin-svgr
// vite.config.ts
import svgr from 'vite-plugin-svgr'
export default defineConfig({ plugins: [svgr()] })
import Logo from './logo.svg?react'

export default () => <Logo width={32} />

7.3 JSON import

// 全体 import
import data from './data.json'

// Named import で Tree Shaking
import { version, name } from './package.json'

// クエリで生文字列
import jsonText from './data.json?raw'

7.4 WASM import

// ?init で WebAssembly.Instance のファクトリ
import init from './add.wasm?init'

const { exports } = await init()
console.log((exports.add as Function)(1, 2)) // 3

7.5 Web Worker import

// worker.ts
self.onmessage = (e) => {
  const result = e.data * 2
  self.postMessage(result)
}
// main.ts
import MyWorker from './worker.ts?worker'

const worker = new MyWorker()
worker.postMessage(21)
worker.onmessage = (e) => console.log(e.data) // 42

// inline 化(別ファイルを発行しない)
import InlineWorker from './worker.ts?worker&inline'

7.6 Shared Worker

import SharedWorker from './shared.ts?sharedworker'
const w = new SharedWorker()
w.port.start()

8. import.meta.glob と dynamic import

8.1 import.meta.glob(複数ファイル一括 import)

// 遅延 import(デフォルト)
const modules = import.meta.glob('./pages/*.tsx')
// { './pages/Home.tsx': () => import('./pages/Home.tsx'), ... }

// 即時 import
const eager = import.meta.glob('./pages/*.tsx', { eager: true })

// raw / url を取得
const md = import.meta.glob('./posts/*.md', { query: '?raw', import: 'default' })

8.2 ルーティングへの応用

// src/router.ts
const pages = import.meta.glob('./pages/**/*.tsx')

export const routes = Object.entries(pages).map(([file, loader]) => {
  const path = file
    .replace('./pages', '')
    .replace(/.tsx$/, '')
    .replace(//index$/, '/')
  return { path, loader }
})

8.3 dynamic import(コード分割)

// ボタンクリックで遅延ロード
button.addEventListener('click', async () => {
  const { default: Chart } = await import('./Chart')
  new Chart(canvas).render()
})

// React.lazy と組み合わせ
import { lazy, Suspense } from 'react'
const Heavy = lazy(() => import('./Heavy'))

9. HMR(Hot Module Replacement)

9.1 HMR は default で有効

React / Vue / Svelte の公式プラグインを入れている場合、状態を保持したコンポーネント差し替えが自動的に動きます。ただしカスタムモジュール(設定ファイルや純粋な util)はデフォルトでフルリロードされます。

9.2 import.meta.hot API でカスタム HMR

// store.ts
export const state = { count: 0 }

if (import.meta.hot) {
  import.meta.hot.accept((newMod) => {
    if (newMod) Object.assign(state, newMod.state)
  })
}

9.3 dispose で副作用クリーンアップ

let timer: number
function start() {
  timer = window.setInterval(() => console.log('tick'), 1000)
}
start()

if (import.meta.hot) {
  import.meta.hot.dispose(() => clearInterval(timer))
  import.meta.hot.accept()
}

9.4 HMR を完全無効化

// vite.config.ts
export default defineConfig({
  server: { hmr: false },
})

10. base path / 静的ホスティング

10.1 サブパス配信(GitHub Pages 等)

// vite.config.ts
export default defineConfig({
  base: '/my-app/',        // https://example.com/my-app/ で配信する場合
})

10.2 環境ごとに base を切り替える

export default defineConfig(({ mode }) => ({
  base: mode === 'production' ? '/my-app/' : '/',
}))

10.3 ビルド成果物の確認

npm run build
npm run preview            # dist/ を静的に配信(本番相当)

11. Rolldown(2026 年の新ビルダ)

Rolldown は Rust 製の Rollup 互換バンドラで、Vite 6.x 系から段階的に rolldown-vite として組み込まれています。Rollup と API 互換を保ちつつ、本番ビルドを 5〜20 倍高速化します。

11.1 rolldown-vite を試す

npm i -D rolldown-vite
// package.json — vite を rolldown-vite に差し替え
{
  "scripts": {
    "dev": "rolldown-vite",
    "build": "rolldown-vite build",
    "preview": "rolldown-vite preview"
  }
}

11.2 Rollup プラグイン互換

// rollup プラグインはそのまま動く
import legacy from '@rollup/plugin-legacy'

export default defineConfig({
  plugins: [legacy()],
})

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

12.1 SSR エントリの分割

// src/entry-client.tsx
import { hydrateRoot } from 'react-dom/client'
import App from './App'
hydrateRoot(document.getElementById('app')!, <App />)
// src/entry-server.tsx
import { renderToString } from 'react-dom/server'
import App from './App'
export function render(url: string) {
  return renderToString(<App url={url} />)
}

12.2 Express から Vite middleware を呼ぶ

// server.ts
import express from 'express'
import { createServer as createViteServer } from 'vite'
import fs from 'node:fs/promises'

const app = express()
const vite = await createViteServer({
  server: { middlewareMode: true },
  appType: 'custom',
})
app.use(vite.middlewares)

app.use('*', async (req, res) => {
  let template = await fs.readFile('index.html', 'utf-8')
  template = await vite.transformIndexHtml(req.originalUrl, template)
  const { render } = await vite.ssrLoadModule('/src/entry-server.tsx')
  const html = template.replace('<!--ssr-outlet-->', render(req.originalUrl))
  res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
})

app.listen(3000)

12.3 ssr.external / noExternal

// vite.config.ts
export default defineConfig({
  ssr: {
    noExternal: ['lodash-es', '@radix-ui/*'],
    external: ['pg', 'sharp'],
  },
})

13. SSG(静的サイト生成)

13.1 vite-ssg(Vue 系)

npm i -D vite-ssg
{
  "scripts": {
    "build": "vite-ssg build"
  }
}

13.2 React なら vite-plugin-ssr-react / react-router の prerender

// vite.config.ts(react-router prerender)
import { reactRouter } from '@react-router/dev/vite'

export default defineConfig({
  plugins: [reactRouter()],
})

14. Vitest 連携(同じ vite.config をテストにも使う)

npm i -D vitest @vitest/ui jsdom @testing-library/react
// vite.config.ts
/// <reference types="vitest" />
import { defineConfig } from 'vite'

export default defineConfig({
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: ['./src/test/setup.ts'],
    coverage: {
      provider: 'v8',
      reporter: ['text', 'html'],
    },
  },
})
// src/components/Button.test.tsx
import { render, screen } from '@testing-library/react'
import { Button } from './Button'

test('renders label', () => {
  render(<Button label="保存" />)
  expect(screen.getByText('保存')).toBeInTheDocument()
})

15. 主要ホスティングへのデプロイ

15.1 Vercel

// vercel.json
{
  "buildCommand": "vite build",
  "outputDirectory": "dist",
  "rewrites": [{ "source": "/(.*)", "destination": "/" }]
}

15.2 Netlify

# netlify.toml
[build]
  command = "vite build"
  publish = "dist"

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200

15.3 Cloudflare Pages

# Build command
npm run build
# Build output directory
dist
# _redirects(SPA フォールバック)
/*  /index.html  200

15.4 GitHub Pages

# .github/workflows/deploy.yml
name: Deploy
on: { push: { branches: [main] } }
jobs:
  build:
    runs-on: ubuntu-latest
    permissions: { contents: read, pages: write, id-token: write }
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - run: npm ci && npm run build
      - uses: actions/upload-pages-artifact@v3
        with: { path: dist }
      - uses: actions/deploy-pages@v4

15.5 さくら / レンタルサーバの静的ホスティング

# dist/ をそのまま FTP / SCP でアップロード
npm run build
scp -r dist/* user@server:/home/user/www/my-app/

16. Webpack からの移行

16.1 webpack.config.js → vite.config.ts 対応表

// webpack.config.js (旧)
module.exports = {
  entry: './src/index.tsx',
  resolve: { alias: { '@': path.resolve(__dirname, 'src') } },
  module: {
    rules: [
      { test: /.tsx?$/, use: 'ts-loader' },
      { test: /.css$/, use: ['style-loader', 'css-loader'] },
      { test: /.png$/, type: 'asset/resource' },
    ],
  },
  devServer: { port: 3000, proxy: { '/api': 'http://localhost:8080' } },
}
// vite.config.ts (新) — ローダー設定はほぼ不要
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'node:path'

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: { '@': path.resolve(__dirname, 'src') },
  },
  server: {
    port: 3000,
    proxy: { '/api': 'http://localhost:8080' },
  },
})

16.2 process.env を import.meta.env に置換

// before
const url = process.env.REACT_APP_API
// after
const url = import.meta.env.VITE_API

16.3 require → import に置換

// before
const data = require('./data.json')
// after
import data from './data.json'

// 動的 require → dynamic import
// before: const m = require(`./locales/${lang}.json`)
const m = await import(`./locales/${lang}.json`)

16.4 CommonJS 依存への対処

// vite.config.ts — CJS-only パッケージを optimizeDeps に追加
export default defineConfig({
  optimizeDeps: {
    include: ['old-cjs-lib', 'another-cjs-lib'],
  },
  build: {
    commonjsOptions: { transformMixedEsModules: true },
  },
})

16.5 移行手順サマリ

  1. index.html をプロジェクトルートに移動し、<script type="module" src="/src/main.tsx"> を追加
  2. public/ 直下に favicon.ico 等の静的ファイル
  3. process.env.XXXimport.meta.env.VITE_XXX へ一括置換
  4. requireimport に置換(動的 require は dynamic import)
  5. webpack-dev-server の proxy / port を server に移植
  6. CleanWebpackPlugin は不要(Vite は毎回 outDir を消去)

17. パフォーマンスチューニング

17.1 optimizeDeps で事前バンドル制御

// vite.config.ts
export default defineConfig({
  optimizeDeps: {
    include: ['react', 'react-dom', 'lodash-es'],
    exclude: ['@some/local-pkg'],
    esbuildOptions: {
      target: 'es2022',
    },
  },
})

17.2 build.rollupOptions.output.manualChunks で分割

build: {
  rollupOptions: {
    output: {
      manualChunks(id) {
        if (id.includes('node_modules')) {
          if (id.includes('react')) return 'react-vendor'
          if (id.includes('chart')) return 'chart-vendor'
          return 'vendor'
        }
      },
    },
  },
}

17.3 brotli / gzip 圧縮プラグイン

npm i -D vite-plugin-compression
import compression from 'vite-plugin-compression'

export default defineConfig({
  plugins: [
    compression({ algorithm: 'brotliCompress', ext: '.br' }),
    compression({ algorithm: 'gzip' }),
  ],
})

17.4 バンドルサイズ可視化

npm i -D rollup-plugin-visualizer
import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig({
  plugins: [
    visualizer({ open: true, filename: 'stats.html', gzipSize: true }),
  ],
})

18. esbuild との関係

Vite は 開発時の依存事前バンドルTS/JSX のトランスパイル に esbuild を使っています。本番ビルドは Rollup(または Rolldown)担当です。

// vite.config.ts — esbuild に直接オプションを渡す
export default defineConfig({
  esbuild: {
    target: 'es2022',
    drop: ['console', 'debugger'],   // 本番から console を除去
    legalComments: 'none',
    jsxInject: `import React from 'react'`,
  },
})

19. アンチパターンと落とし穴

19.1 NG: dev サーバが遅いのに optimizeDeps を放置

// 初回起動が 10 秒以上かかるなら、頻出依存を明示的に include
optimizeDeps: {
  include: [
    'react', 'react-dom', 'react-router',
    'lodash-es/debounce', 'lodash-es/cloneDeep',
  ],
}

19.2 NG: 巨大ライブラリの全体 import

// 悪い例(本番バンドルに lodash 全部入る)
import _ from 'lodash'
_.debounce(fn, 200)

// 良い例(named / ESM 版)
import { debounce } from 'lodash-es'
debounce(fn, 200)

19.3 NG: process.env を本番で参照

// ブラウザでは undefined。必ず import.meta.env を使う
console.log(process.env.NODE_ENV)   // ❌
console.log(import.meta.env.MODE)   // ✅

19.4 NG: public/ に大量の動的アセット

public/ はそのままコピーされ、ハッシュも付かないためキャッシュバスティングが効きません。動的に参照する画像は src/assets/ から import するのが原則です。

19.5 NG: SSR で window を直接参照

// SSR でビルドが落ちる
const w = window.innerWidth   // ❌

// useEffect 内 / typeof チェック
if (typeof window !== 'undefined') {
  // ...
}

20. まとめ・次に学ぶこと

本記事では Vite 6.x のインストールから vite.config.ts、プラグイン、HMR、SSR/SSG、Webpack 移行、Rolldown、Lightning CSS まで 40 を超える実コードブロック で一気に解説しました。重要ポイントを再掲します。

  • 開発時は esbuild + ネイティブ ESM、本番は Rollup(→ Rolldown)という二段構え
  • 環境変数は VITE_ プレフィックス必須・import.meta.env で参照
  • プラグインは Rollup プラグイン互換 + Vite 固有フック(configureServer / transformIndexHtml / handleHotUpdate)
  • Webpack 移行は「ローダーを捨てて plugins と optimizeDeps に集約」が基本方針
  • 2026 年は Rolldown / Lightning CSS を本番投入できる成熟期に入っている

さらに踏み込みたい方には Next.js 15 完全実践ガイドRemix (React Router v7) 完全実践ガイド が良い次の一歩です。React Server Components や Server Actions と Vite ベースの SPA / SSR 構成を比較すると、用途に応じた選定眼が身につきます。

「もっと体系的にモダンフロントを学びたい」「Vite を実務で使えるレベルまで一気に伸ばしたい」という方には、以下のようなオンライン学習サービスも選択肢になります。いずれも無料カウンセリングや無料体験から始められます。

  • テックアカデミー — 現役エンジニアによるマンツーマンメンタリング。フロントエンドコースで React / Vue / Vite を扱う
  • 侍エンジニア — 専属講師の完全オーダーメイドカリキュラム。Vite + 任意フレームワークで自作ポートフォリオを作る学習に向く
  • DMM WEBCAMP — 短期集中で実務レベルを目指すなら。転職保証コースもあり
  • レバテック — すでに開発経験がある人向けの転職エージェント。Vite / Next.js を実務で使う求人も豊富

ツールは目的ではなく手段です。Vite を入口に、最終的にユーザに価値を届けるプロダクトを作り切れるエンジニアを目指していきましょう。

コメント

タイトルとURLをコピーしました