0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LaravelのWebpack MixからViteへの移行した話

Last updated at Posted at 2024-08-18

LaravelのWebpack MixからViteへの移行した話

この記事では、Laravelのプロジェクトにおいて、Webpack MixからViteへの移行プロセスとその結果について詳しく解説します。Laravel 9以降、Viteが標準のビルドツールとして採用されており、この記事ではその移行理由と実際の移行手順、得られた効果について紹介します。
基本的には公式ドキュメント通りに進め、プロジェクト環境によるエラーがあれば解決する形で対応しました。

環境

機能 移行前 移行後
Laravel 9.23.0 9.23.0
Vue 3.2.37 3.2.37
ビルドツール Laravel Mix 6.0.0 Vite 5.4.0

移行前の課題と結果

  • ビルド時間の長さ

    • 移行前: npm run devnpm run build の実行時間が約3分。
    • 移行後: npm run dev の実行時間が1秒以下、npm run build の実行時間が10秒に短縮されました。
  • 開発効率の低下

    • 移行前: Laravel Mixでのビルドに時間がかかり、開発効率が低下していました。
    • 移行後: HMR(ホットモジュールリプレースメント)がより効率的に動作し、差分更新やキャッシュを考慮した迅速な更新が可能になりました。
  • 標準ツールの推奨

    • Laravel 9以降は、Laravel MixからViteへの移行が推奨されており、標準のビルドツールとしてViteの採用が必要です。

公式ドキュメント

Laravel9 Vite

Vite切り替え移行ガイド

移行対応

1. ViteとLaravelプラグインをインストールする

viteインストール

npm install --save-dev vite laravel-vite-plugin

Vite プラグインをインストール

npm install --save-dev @vitejs/plugin-vue

packageインストール時のエラーと解決

私の環境では、Viteインストール時に@types/nodeの依存関係と競合してインストールできないエラーが発生。

エラーログと解決詳細
npm error code ERESOLVE
npm error ERESOLVE could not resolve
npm error
npm error While resolving: vite@5.4.0
npm error Found: @types/node@17.0.45
npm error node_modules/@types/node
npm error   @types/node@"*" from @types/body-parser@1.19.2
npm error   node_modules/@types/body-parser
npm error     @types/body-parser@"*" from @types/express@4.17.13
npm error     node_modules/@types/express
npm error       @types/express@"*" from @types/serve-index@1.9.1
npm error       node_modules/@types/serve-index
npm error         @types/serve-index@"^1.9.1" from webpack-dev-server@4.9.3
npm error         node_modules/webpack-dev-server
npm error       2 more (http-proxy-middleware, webpack-dev-server)
npm error   @types/node@"*" from @types/bonjour@3.5.10
npm error   node_modules/@types/bonjour
npm error     @types/bonjour@"^3.5.9" from webpack-dev-server@4.9.3
npm error     node_modules/webpack-dev-server
npm error       webpack-dev-server@"^4.7.3" from laravel-mix@6.0.49
npm error       node_modules/laravel-mix
npm error         dev laravel-mix@"^6.0.0-beta.17" from the root project
npm error   12 more (@types/clean-css, @types/connect, ...)
npm error
npm error Could not resolve dependency:
npm error peerOptional @types/node@"^18.0.0 || >=20.0.0" from vite@5.4.0
npm error node_modules/vite
npm error   vite@"*" from the root project
npm error   peer vite@"^5.0.0" from laravel-vite-plugin@1.0.5
npm error   node_modules/laravel-vite-plugin
npm error     laravel-vite-plugin@"*" from the root project
npm error
npm error Conflicting peer dependency: @types/node@22.3.0
npm error node_modules/@types/node
npm error   peerOptional @types/node@"^18.0.0 || >=20.0.0" from vite@5.4.0
npm error   node_modules/vite
npm error     vite@"*" from the root project
npm error     peer vite@"^5.0.0" from laravel-vite-plugin@1.0.5
npm error     node_modules/laravel-vite-plugin
npm error       laravel-vite-plugin@"*" from the root project
npm error
npm error Fix the upstream dependency conflict, or retry
npm error this command with --force or --legacy-peer-deps
npm error to accept an incorrect (and potentially broken) dependency resolution.
npm error
npm error
npm error For a full report see:
npm error /home/sail/.npm/_logs/2024-08-14T07_53_26_297Z-eresolve-report.txt
npm error A complete log of this run can be found in: /home/sail/.npm/_logs/2024-08-14T07_53_26_297Z-debug-0.log

解決策:@types/node のバージョンを更新する

vite が依存している @types/node のバージョンと競合しないように、プロジェクト内の @types/node のバージョンを 18.x 以上に更新してから、再度インストールを行う。

npm install @types/node@18 --save-dev

を実行後に再度viteインストールをすると成功

2. Vite 設定ファイルの作成

touch vite.config.js
transformAssetUrlsは、npm run buildで画像パスを読み込む際にエラーが発生していたため追加しました。

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
  plugins: [
    vue({
          // This is needed, or else Vite will try to find image paths (which it wont be able to find because this will be called on the web, not directly)
          // For example <img src="/images/logo.png"> will not work without the code below
          template: {
            transformAssetUrls: {
              includeAbsolute: false
            }
          }
        }),
    laravel({
      input: [
        // TypeScript files
        'resources/js/app.ts',
        'resources/admin/js/adminapp.ts',

        // CSS files
        'resources/admin/css/adminapp.css',
        'resources/css/app.css',
        'resources/css/reset.css',
      ],
      refresh: true,
    }),
  ],
  resolve: {
    alias: {
      '/@': resolve(__dirname, 'resources'),
    }
  },
  build: {
    rollupOptions: {
      output: {
        entryFileNames: 'js/[name].[hash].js',
        chunkFileNames: 'js/[name].[hash].js',
        assetFileNames: 'assets/[name].[hash].[ext]',
      },
    },
  },
});

3. package.jsonのscriptを変更

    "scripts": {
        "dev": "vite",
        "build": "vite build",
        "lint:script": "eslint --ext .ts,.vue --ignore-path .gitignore .",
        "fix:eslint": "eslint --ext .ts,.vue --fix --ignore-path .gitignore ."
    }

4. Bladeファイルの更新

Bladeファイル内で、Viteのエントリーポイントを参照するように変更します。Viteは、Laravel Mixのような mix() 関数ではなく、@vite() ディレクティブを使用します。

@vite(['resources/js/app.ts', 'resources/css/app.css'])

// 移行前サンプル
{{-- admin.blade.php --}}
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>サイトタイトル</title>

    <!-- Fonts -->
    <link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">
    <link href="{{ mix('css/reset.css') }}" rel="stylesheet">
    <link href="{{ mix('admin/css/adminapp.css') }}" rel="stylesheet">
    @if(config('app.env') === 'local')
    <script src="{{ mix('admin/js/adminapp.bundle.js') }}" defer></script>
    @else
    <script src="{{ mix('admin/js/adminapp.production.js') }}" defer></script>
    @endif
</head>

<body>
    <div id="admin-app"></div>
</body>

</html>
// 移行後サンプル
{{-- student.blade.php --}}
@vite(['resources/js/app.ts', 'resources/css/app.css'])

{{-- admin.blade.php --}}
@vite(['resources/admin/js/adminapp.ts', 'resources/css/reset.css', 'resources/admin/css/adminapp.css'])

5. ビルド実行 テスト

npm run dev実行後に下記のエラーと解決

[ERROR] "laravel-vite-plugin" resolved to an ESM file. ESM file cannot be loaded by require. See https://vitejs.dev/guide/troubleshooting.html#this-package-is-esm-only for more details. [plugin externalize-deps]

指定されたドキュメント確認後、下記をpackage.jsonに追加
"type": "module”

6. ポート解放

${VITE_PORT:-5173}:${VITE_PORT:-5173}を追加

version: "3"
services:
    laravel.test:
        build:
            context: ./docker/8.0
            dockerfile: Dockerfile
            args:
                WWWGROUP: "${WWWGROUP}"
        image: sail-8.0/app
        extra_hosts:
            - "host.docker.internal:host-gateway"
        ports:
            - "${APP_PORT:-80}:80"
            - "${VITE_PORT:-5173}:${VITE_PORT:-5173}"

なぜ5173を追加すると動くのか?

5173ポートを追加すると動作する理由は、Viteが開発サーバーとして動作する際に、デフォルトで使用するポートが 5173 であるためです。具体的な理由は以下の通りです。

詳細はこちら

Viteの動作とポート 5173

  1. Viteの開発サーバー:
    • Viteはフロントエンド開発のための高速なビルドツールであり、開発環境では開発サーバーを立ち上げて、ファイルのホットリロードやリアルタイムでの更新を行います。この開発サーバーは通常、デフォルトでポート 5173 を使用します。
  2. Dockerコンテナ内でのViteサーバー:
    • Dockerコンテナ内でViteサーバーを動作させる場合、コンテナ内のポート 5173 でViteサーバーが立ち上がりますが、これをホストマシン(ローカルPC)からアクセスするためには、そのポートを外部に公開する必要があります。
  3. 5173ポートの追加による効果:
    • docker-compose.ymlports セクションで 5173 ポートを公開する設定を行うことで、コンテナ内のViteサーバー(ポート 5173)に対するリクエストがホストマシンのポート 5173 を通じてアクセスできるようになります。これにより、ホストマシンのブラウザから http://localhost:5173 にアクセスすることで、Viteが提供するリソース(JS/CSSなど)にアクセスできるようになります。

設定が必要な理由

  • ホットリロード機能: Viteは開発時にファイルの変更を即座に反映させるホットリロード機能を提供します。この機能が有効に動作するためには、ブラウザとViteサーバーが直接通信できるようにする必要があります。ポート 5173 を公開することで、Viteのホットリロード機能が正しく機能します。

Dockerコンテナ内の動作を理解する

  • Dockerコンテナ内で動作しているサーバー(この場合はVite)は、コンテナ内で指定されたポートを使用します。そのポートが外部(ホストマシン)からアクセス可能でなければ、ホスト側からコンテナ内のViteサーバーにアクセスできません。そのため、docker-compose.yml でポートのマッピング(例: 5173:5173)を行う必要があります。

この設定によって、ホストマシンのブラウザから http://localhost:5173 でViteサーバーにアクセスできるようになり、フロントエンドの開発環境が正しく動作するようになります。

7. tailwind.config.jsの修正

  • ファイル名を .cjs に変更: tailwind.config.jstailwind.config.cjs にリネームすることで、このファイルが CommonJS として扱われ、問題を回避できます。

mv tailwind.config.js tailwind.config.cjs

8. svgファイルの読み込みエラー

vite.config.jsに以下を追加することで解決

publicDir: 'public', *// 公開ディレクトリを指定*

参考:Vite公式ドキュメント

9. CommonJSのモジュール内容をESモジュールに書き換え

require は CommonJS のモジュールシステムの一部ですが、Vite は ES モジュールを前提としているため、import を使用することが推奨されます。

// Before
require("./bootstrap");
require("./fontawsome");
// After
import './bootstrap';
import './fontawsome';

CommonJS、 ESModulesとは??

CommonJSESModules は、JavaScriptのモジュールシステムを提供する二つの異なる方式です。モジュールシステムは、コードをモジュール(ファイル)に分割して再利用しやすくするための仕組みを提供します。以下に、それぞれの特徴と違いを説明します。

詳細はこちら

CommonJS

  • 概要: CommonJS は、Node.js で使用されるモジュールシステムです。サーバーサイドJavaScriptのモジュールの標準化を目指して設計されました。

  • 主な特徴:

    • require 関数: 他のモジュールをインポートするために require 関数を使用します。
    • module.exports オブジェクト: モジュールをエクスポートするために module.exports を使用します。
    • 同期的: require は同期的にモジュールを読み込むため、スクリプトの実行がブロックされる場合があります。これはサーバーサイドの環境では問題ありませんが、ブラウザ環境では非効率になることがあります。
    • CommonJS サンプル

    math.js:

        function add(a, b) {
          return a + b;
        }
        
        module.exports = {
          add,
        };
    

    main.js:

        const math = require('./math.js');
    

ESModules (ESM)

  • 概要: ESModules(ESM)は、JavaScriptの公式なモジュールシステムです。ECMAScript 2015 (ES6) で導入され、Node.js やブラウザなど、広くサポートされています。

  • 主な特徴:

    • import キーワード: 他のモジュールをインポートするために import を使用します。
    • export キーワード: モジュールをエクスポートするために export を使用します。
    • 非同期的: ESModulesは非同期的にモジュールをロードするため、ブラウザ環境でも効率的に動作します。
    • 静的解析可能: importexport はファイルの先頭で宣言されるため、JavaScriptエンジンがスクリプトを解析する際に、依存関係を事前に把握できます。
    • ESModules (ESM) サンプル

    math.js:

        export function add(a, b) {
          return a + b;
        }
    

    main.js:

        import { add } from './math.js';
        console.log(add(2, 3)); // 出力: 5
    

違い

  • 書き方: CommonJSでは requiremodule.exports を使いますが、ESModulesでは importexport を使います。
  • 実行環境: CommonJS は主にNode.jsで使用され、ESModules はブラウザや最新のNode.jsで使用されます。
  • 同期 vs 非同期: CommonJSは同期的にモジュールをロードし、ESModulesは非同期的にモジュールをロードします。
  • 静的 vs 動的: ESModulesは静的(スクリプトの解析時に決定される)、CommonJSは動的(スクリプトの実行時に決定される)です。

プロジェクトのモジュールシステムの判定方法

  • package.json ファイルに "type": "module" がある場合、そのプロジェクトはESModulesとして扱われます。
  • require/module.exports が使われている場合 はCommonJS、import/export が使われている場合 はESModulesです。

この二つのモジュールシステムは、JavaScriptの環境によって選択され、使い分けられることがありますが、Node.jsの最新版では両方がサポートされています。

10. Laravel Mixの削除

Laravel MixのパッケージやMixによってビルドされていたファイルは不要になるため削除しました。
その他Mixで使用していた環境変数などもViteに移行しました。
環境によって使用有無があるかと思いますので、環境に応じて必要な移行対応を実施ください。

まとめ

LaravelのビルドツールをLaravel MixからViteに移行することで、開発効率の向上とビルド時間の大幅な短縮が実現しました。移行前のLaravel Mixでは、npm run devnpm run build の実行に時間がかかり、開発のスピードに影響を及ぼしていましたが、Viteに移行後はこれらのビルド時間が大幅に短縮され、開発プロセスがスムーズになりました。

具体的には、npm run dev の実行時間が約3分から1秒以下に、npm run build の実行時間が約3分から10秒に短縮されました。また、HMR(ホットモジュールリプレースメント)の効率化により、差分更新やキャッシュを考慮した迅速な開発が可能となり、開発者の生産性が向上しました。

移行対応も数時間ほどでスムーズに移行できたため、Laravel Mixを使用されている方は、是非Viteへの移行を検討してみてください。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?