T-CREATOR

Vite のエラーハンドリング:よくあるトラブルと対処法

Vite のエラーハンドリング:よくあるトラブルと対処法

Vite 開発を始めたばかりの頃、エラーメッセージを見て途方に暮れた経験はありませんか?「このエラー、どうすれば解決できるんだろう...」と悩んだ日々を思い出します。

Vite は高速で使いやすいビルドツールですが、エラーが発生した時の対処法を理解していないと、開発効率が大きく下がってしまいます。この記事では、Vite 開発でよく遭遇するエラーとその解決方法を段階的に学んでいきましょう。

エラーハンドリングのスキルを身につけることで、開発時間を大幅に短縮し、より快適なコーディングライフを送ることができます。一緒に Vite のエラーと向き合い、解決の喜びを味わいましょう。

Vite エラーハンドリングの基礎知識

Vite のエラーは大きく 3 つのカテゴリに分類できます。それぞれの特徴を理解することで、エラーの原因を素早く特定できるようになります。

Vite のエラー分類

ビルドエラー

  • プロジェクトのビルド時に発生
  • コードの構文エラーや設定ミスが原因
  • 開発サーバー起動時や本番ビルド時に確認可能

ランタイムエラー

  • ブラウザでアプリケーション実行中に発生
  • JavaScript の実行時エラーや API 呼び出しエラー
  • ブラウザの開発者ツールで確認

開発サーバーエラー

  • Vite の開発サーバー自体の問題
  • ポート競合やファイル監視の問題
  • ターミナルにエラーメッセージが表示

エラーメッセージの読み方

Vite のエラーメッセージは構造化されており、重要な情報が含まれています。

bash[vite] Internal server error: ENOENT: no such file or directory, open '/path/to/missing/file.js'
    at Object.openSync (fs.js:476:3)
    at Object.readFileSync (fs.js:377:35)
    at loadConfigFromFile (/node_modules/vite/dist/node/chunks/dep-abc123.js:123:45)

このエラーメッセージから読み取れる情報:

  • ENOENT: ファイルが見つからないエラー
  • 問題のファイルパス: ​/​path​/​to​/​missing​/​file.js
  • エラーが発生した場所: loadConfigFromFile関数内

デバッグツールの活用方法

Vite には強力なデバッグ機能が組み込まれています。

詳細ログの有効化

bash# 開発サーバー起動時に詳細ログを表示
yarn dev --debug

# 特定のログレベルを指定
yarn dev --logLevel debug

ブラウザ開発者ツールの活用

javascript// コンソールでViteの状態を確認
console.log(import.meta.env);
console.log(import.meta.hot);

よくあるエラーパターンと対処法

実際の開発現場で頻繁に遭遇するエラーとその解決方法を見ていきましょう。これらのエラーを理解することで、問題解決のスピードが格段に向上します。

モジュール解決エラー

パス解決の問題

エラー例:

bash[vite] Failed to resolve module specifier "~/components/Button"
  from /src/App.vue

原因と対処法: このエラーは、相対パスやエイリアスの設定が正しくない場合に発生します。

javascript// vite.config.js - エイリアス設定の例
import { defineConfig } from 'vite';
import { resolve } from 'path';

export default defineConfig({
  resolve: {
    alias: {
      '~': resolve(__dirname, 'src'),
      '@': resolve(__dirname, 'src'),
      '@components': resolve(__dirname, 'src/components'),
    },
  },
});

TypeScript での型定義

typescript// tsconfig.json - パスマッピングの設定
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "~/*": ["src/*"],
      "@/*": ["src/*"],
      "@components/*": ["src/components/*"]
    }
  }
}

依存関係の競合

エラー例:

bash[vite] Conflicting dependencies:
  - react@^18.0.0 (from /package.json)
  - react@^17.0.0 (from /node_modules/other-package/package.json)

解決方法: 依存関係の競合は、パッケージマネージャーの解決機能を活用します。

bash# 依存関係の競合を確認
yarn why react

# 強制的に解決
yarn install --force

# 依存関係を整理
yarn dedupe

package.json での解決例:

json{
  "resolutions": {
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  }
}

TypeScript 型定義エラー

エラー例:

bash[vite] TypeScript error: Cannot find module 'lodash' or its corresponding type declarations.

解決方法: 型定義パッケージのインストールが必要です。

bash# 型定義パッケージをインストール
yarn add -D @types/lodash

# または、型定義が含まれていない場合
yarn add -D @types/node

型定義ファイルの作成例:

typescript// src/types/global.d.ts
declare module '*.vue' {
  import type { DefineComponent } from 'vue';
  const component: DefineComponent<{}, {}, any>;
  export default component;
}

declare module 'lodash' {
  export function debounce<
    T extends (...args: any[]) => any
  >(func: T, wait?: number): T;
}

ビルド設定エラー

プラグイン設定ミス

エラー例:

bash[vite] Plugin error: [vite:vue] Failed to parse template:
  Unexpected token (1:1) in /src/components/Example.vue

原因と対処法: Vue プラグインの設定が正しくない場合に発生します。

javascript// vite.config.js - 正しいVueプラグイン設定
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  plugins: [
    vue({
      // テンプレートコンパイラーオプション
      template: {
        compilerOptions: {
          isCustomElement: (tag) => tag.startsWith('my-'),
        },
      },
    }),
  ],
});

React プラグインの設定例:

javascript// vite.config.js - Reactプラグイン設定
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [
    react({
      // JSXの設定
      jsxRuntime: 'automatic',
      // Fast Refreshの設定
      fastRefresh: true,
    }),
  ],
});

環境変数の問題

エラー例:

bash[vite] Environment variable VITE_API_URL is not defined

解決方法: 環境変数の設定と読み込み方法を確認します。

bash# .env.local ファイルの作成
VITE_API_URL=https://api.example.com
VITE_APP_TITLE=My Vite App

環境変数の読み込み例:

javascript// 環境変数の使用例
const apiUrl = import.meta.env.VITE_API_URL;
const appTitle = import.meta.env.VITE_APP_TITLE;

console.log('API URL:', apiUrl);
console.log('App Title:', appTitle);

TypeScript での型定義:

typescript// src/env.d.ts - 環境変数の型定義
/// <reference types="vite/client" />

interface ImportMetaEnv {
  readonly VITE_API_URL: string;
  readonly VITE_APP_TITLE: string;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

アセット処理エラー

エラー例:

bash[vite] Failed to load module /src/assets/image.png:
  Invalid module format

解決方法: アセットのインポート方法と設定を確認します。

javascript// vite.config.js - アセット処理の設定
import { defineConfig } from 'vite';

export default defineConfig({
  assetsInclude: ['**/*.png', '**/*.jpg', '**/*.svg'],
  build: {
    rollupOptions: {
      output: {
        assetFileNames: (assetInfo) => {
          const info = assetInfo.name.split('.');
          const ext = info[info.length - 1];
          if (/png|jpe?g|svg|gif|tiff|bmp|ico/i.test(ext)) {
            return `assets/images/[name]-[hash][extname]`;
          }
          return `assets/[name]-[hash][extname]`;
        },
      },
    },
  },
});

アセットの正しいインポート方法:

javascript// 画像のインポート例
import logo from '@/assets/logo.png';
import icon from '@/assets/icon.svg';

// CSSでの背景画像使用
import '@/assets/background.jpg';

開発サーバーエラー

ポート競合

エラー例:

bash[vite] Server is running at http://localhost:3000
[vite] Port 3000 is already in use, trying 3001...

解決方法: ポート競合を解決する方法をいくつか紹介します。

bash# 特定のポートを指定して起動
yarn dev --port 3001

# 利用可能なポートを自動選択
yarn dev --port 0

# 既存のプロセスを終了
lsof -ti:3000 | xargs kill -9

vite.config.js での設定:

javascript// vite.config.js - サーバー設定
import { defineConfig } from 'vite';

export default defineConfig({
  server: {
    port: 3000,
    strictPort: false, // ポートが使用中の場合、次のポートを試行
    host: true, // ネットワークアクセスを許可
  },
});

ホットリロードの問題

エラー例:

bash[vite] hmr update failed: Cannot find module './Component.vue'

解決方法: ホットリロードの問題は、ファイル監視の設定で解決できます。

javascript// vite.config.js - HMR設定
import { defineConfig } from 'vite';

export default defineConfig({
  server: {
    hmr: {
      overlay: true, // エラーオーバーレイを表示
      timeout: 5000, // タイムアウト設定
    },
    watch: {
      usePolling: true, // ポーリング監視を使用
      interval: 1000, // ポーリング間隔
    },
  },
});

HMR の手動制御例:

javascript// カスタムHMRハンドラー
if (import.meta.hot) {
  import.meta.hot.accept((newModule) => {
    if (newModule) {
      // モジュール更新時の処理
      console.log('Module updated:', newModule);
    }
  });
}

プロキシ設定エラー

エラー例:

bash[vite] Proxy error: connect ECONNREFUSED 127.0.0.1:8080

解決方法: API プロキシの設定を正しく行います。

javascript// vite.config.js - プロキシ設定
import { defineConfig } from 'vite';

export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        secure: false,
        rewrite: (path) => path.replace(/^\/api/, ''),
      },
      '/ws': {
        target: 'ws://localhost:8080',
        ws: true,
      },
    },
  },
});

プロキシ設定の詳細例:

javascript// より詳細なプロキシ設定
export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        configure: (proxy, options) => {
          proxy.on('error', (err, req, res) => {
            console.log('proxy error', err);
          });
          proxy.on('proxyReq', (proxyReq, req, res) => {
            console.log(
              'Sending Request to the Target:',
              req.method,
              req.url
            );
          });
          proxy.on('proxyRes', (proxyRes, req, res) => {
            console.log(
              'Received Response from the Target:',
              proxyRes.statusCode,
              req.url
            );
          });
        },
      },
    },
  },
});

実践的なデバッグ手法

エラーが発生した際の効率的なデバッグ手法を身につけることで、問題解決の時間を大幅に短縮できます。

ログレベルの調整

Vite のログレベルを調整することで、必要な情報だけを表示できます。

bash# ログレベルの指定
yarn dev --logLevel silent    # エラーのみ表示
yarn dev --logLevel error     # エラーと警告を表示
yarn dev --logLevel warn      # 警告以上を表示
yarn dev --logLevel info      # 情報以上を表示
yarn dev --logLevel debug     # デバッグ情報も表示

vite.config.js でのログ設定:

javascript// vite.config.js - ログ設定
import { defineConfig } from 'vite';

export default defineConfig({
  logLevel: 'info',
  clearScreen: false, // 画面クリアを無効化
  customLogger: {
    info: (msg) => console.log(`[INFO] ${msg}`),
    warn: (msg) => console.warn(`[WARN] ${msg}`),
    error: (msg) => console.error(`[ERROR] ${msg}`),
  },
});

エラーバウンダリの実装

React アプリケーションでのエラーバウンダリの実装例です。

jsx// src/components/ErrorBoundary.jsx
import React from 'react';

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }

  componentDidCatch(error, errorInfo) {
    console.error(
      'Error caught by boundary:',
      error,
      errorInfo
    );
  }

  render() {
    if (this.state.hasError) {
      return (
        <div className='error-boundary'>
          <h2>何か問題が発生しました</h2>
          <p>ページを再読み込みしてください</p>
          <button onClick={() => window.location.reload()}>
            再読み込み
          </button>
        </div>
      );
    }

    return this.props.children;
  }
}

export default ErrorBoundary;

Vue.js でのエラーハンドリング例:

vue<!-- src/components/ErrorBoundary.vue -->
<template>
  <div v-if="error" class="error-boundary">
    <h2>エラーが発生しました</h2>
    <p>{{ error.message }}</p>
    <button @click="resetError">再試行</button>
  </div>
  <slot v-else />
</template>

<script>
export default {
  name: 'ErrorBoundary',
  data() {
    return {
      error: null,
    };
  },
  methods: {
    resetError() {
      this.error = null;
    },
  },
  errorCaptured(err, vm, info) {
    this.error = err;
    console.error('Error captured:', err, info);
    return false; // エラーの伝播を停止
  },
};
</script>

カスタムエラーハンドリング

Vite のビルドプロセスにカスタムエラーハンドリングを追加します。

javascript// vite.config.js - カスタムエラーハンドリング
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    {
      name: 'custom-error-handler',
      configureServer(server) {
        server.middlewares.use((req, res, next) => {
          const originalSend = res.send;
          res.send = function (data) {
            if (res.statusCode >= 400) {
              console.error(
                `HTTP ${res.statusCode}: ${req.url}`
              );
            }
            return originalSend.call(this, data);
          };
          next();
        });
      },
      handleHotUpdate({ file, server }) {
        if (file.endsWith('.vue')) {
          console.log(`Vue file updated: ${file}`);
        }
      },
    },
  ],
});

グローバルエラーハンドラーの実装:

javascript// src/utils/errorHandler.js
export class ErrorHandler {
  static init() {
    // 未処理のPromiseエラーをキャッチ
    window.addEventListener(
      'unhandledrejection',
      (event) => {
        console.error(
          'Unhandled promise rejection:',
          event.reason
        );
        event.preventDefault();
      }
    );

    // グローバルエラーをキャッチ
    window.addEventListener('error', (event) => {
      console.error('Global error:', event.error);
    });

    // Vue.jsのグローバルエラーハンドラー
    if (window.Vue) {
      window.Vue.config.errorHandler = (err, vm, info) => {
        console.error('Vue error:', err, info);
      };
    }
  }

  static logError(error, context = {}) {
    const errorInfo = {
      message: error.message,
      stack: error.stack,
      timestamp: new Date().toISOString(),
      context,
    };

    console.error('Error logged:', errorInfo);

    // エラー報告サービスに送信(例:Sentry)
    // this.reportToService(errorInfo)
  }
}

予防的対策とベストプラクティス

エラーが発生する前に予防策を講じることで、開発効率を大幅に向上させることができます。

エラー監視の仕組み

開発環境でのエラー監視システムを構築します。

javascript// src/utils/errorMonitor.js
export class ErrorMonitor {
  constructor() {
    this.errors = [];
    this.maxErrors = 100;
  }

  captureError(error, context = {}) {
    const errorEntry = {
      id: Date.now(),
      error: error.message,
      stack: error.stack,
      context,
      timestamp: new Date().toISOString(),
      userAgent: navigator.userAgent,
      url: window.location.href,
    };

    this.errors.push(errorEntry);

    // エラー数が上限を超えた場合、古いエラーを削除
    if (this.errors.length > this.maxErrors) {
      this.errors.shift();
    }

    // ローカルストレージに保存
    this.persistErrors();

    return errorEntry;
  }

  persistErrors() {
    try {
      localStorage.setItem(
        'vite-errors',
        JSON.stringify(this.errors)
      );
    } catch (e) {
      console.warn('Failed to persist errors:', e);
    }
  }

  loadErrors() {
    try {
      const stored = localStorage.getItem('vite-errors');
      if (stored) {
        this.errors = JSON.parse(stored);
      }
    } catch (e) {
      console.warn('Failed to load errors:', e);
    }
  }

  getErrors() {
    return this.errors;
  }

  clearErrors() {
    this.errors = [];
    localStorage.removeItem('vite-errors');
  }
}

// グローバルインスタンス
export const errorMonitor = new ErrorMonitor();

エラー監視の統合例:

javascript// src/main.js - エラー監視の統合
import { createApp } from 'vue';
import App from './App.vue';
import { errorMonitor } from './utils/errorMonitor';

const app = createApp(App);

// エラー監視を初期化
errorMonitor.loadErrors();

// Vue.jsのグローバルエラーハンドラー
app.config.errorHandler = (err, vm, info) => {
  errorMonitor.captureError(err, {
    component: vm?.$options?.name,
    info,
  });
};

// 未処理のPromiseエラー
window.addEventListener('unhandledrejection', (event) => {
  errorMonitor.captureError(event.reason, {
    type: 'unhandledrejection',
  });
  event.preventDefault();
});

app.mount('#app');

開発環境の統一

チーム開発での環境統一を実現します。

json// .vscode/settings.json - VSCode設定の統一
{
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
  "typescript.preferences.importModuleSpecifier": "relative",
  "emmet.includeLanguages": {
    "vue": "html"
  },
  "files.associations": {
    "*.vue": "vue"
  }
}

ESLint 設定の統一:

javascript// .eslintrc.js - ESLint設定
module.exports = {
  root: true,
  env: {
    node: true,
    browser: true,
    es2021: true,
  },
  extends: [
    'eslint:recommended',
    '@vue/typescript/recommended',
    'plugin:vue/vue3-recommended',
  ],
  parserOptions: {
    ecmaVersion: 2021,
    sourceType: 'module',
  },
  rules: {
    'no-console':
      process.env.NODE_ENV === 'production'
        ? 'warn'
        : 'off',
    'no-debugger':
      process.env.NODE_ENV === 'production'
        ? 'warn'
        : 'off',
    'vue/multi-word-component-names': 'off',
  },
};

Prettier 設定の統一:

json// .prettierrc - Prettier設定
{
  "semi": false,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "es5",
  "printWidth": 80,
  "bracketSpacing": true,
  "arrowParens": "avoid"
}

継続的インテグレーションでの検証

CI/CD パイプラインでのエラー検証を実装します。

yaml# .github/workflows/ci.yml - GitHub Actions設定
name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'yarn'

      - name: Install dependencies
        run: yarn install --frozen-lockfile

      - name: Lint
        run: yarn lint

      - name: Type check
        run: yarn type-check

      - name: Build
        run: yarn build

      - name: Test
        run: yarn test

package.json のスクリプト設定:

json{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
    "type-check": "vue-tsc --noEmit",
    "test": "vitest",
    "test:ui": "vitest --ui",
    "test:coverage": "vitest --coverage"
  }
}

Vite の型チェック設定:

javascript// vite.config.js - 型チェック設定
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  plugins: [vue()],
  build: {
    rollupOptions: {
      onwarn(warning, warn) {
        // 特定の警告を無視
        if (warning.code === 'CIRCULAR_DEPENDENCY') return;
        warn(warning);
      },
    },
  },
});

まとめ

Vite のエラーハンドリングについて、基礎から実践的な対処法まで詳しく解説してきました。

エラーは開発の敵ではなく、成長の糧となるものです。最初はエラーメッセージを見て困惑するかもしれませんが、今回学んだ知識を活用することで、問題解決のスピードが格段に向上するはずです。

重要なポイントを振り返ると:

  1. エラーの分類を理解する - ビルドエラー、ランタイムエラー、開発サーバーエラーの違いを把握
  2. エラーメッセージを読み解く - 構造化されたエラー情報から原因を特定
  3. 段階的にデバッグする - ログレベル調整からカスタムエラーハンドリングまで
  4. 予防策を講じる - エラー監視、環境統一、CI/CD での検証

これらの知識を実践で活用することで、Vite 開発がより快適で効率的なものになります。エラーに遭遇した時は、この記事を参考にして、冷静に対処してください。

開発の道のりでエラーは必ず発生しますが、それを乗り越えるたびに、あなたのスキルは確実に向上します。焦らず、着実に、そして楽しみながら Vite 開発を続けていきましょう。

関連リンク