Laravel9のバージョン9.2からデフォルトのビルドツールがWebpack(Laravel Mix)からViteに変更になりました。Viteはフロントエンドツールとして現在爆発的に人気のあるツールでViteを利用することでフロントエンド開発のスピードを加速させることができます。対象対象はLaravel、Reactを利用した経験がある人なのでLaravel, Reactについての細かな設定は含まれていません。対象はLaravel、Vue.js 3を利用した経験がある人なのでLaravel, Vue.jsについての細かな説明は含まれていません。

Vite環境でVueを利用するためにはいくつかの追加設定が必要なので本文書ではその手順について説明を行っています。

Laravel + Vite + Vue.js 3環境の構築に興味がある人はぜひ参考にしてください。

Inertiaは利用していません。

Laravel + Vite + React環境での設定方法も公開しています。

Laravelプロジェクトの作成

laravelコマンドを利用してLaravelプロジェクトを作成します。プロジェクト名はlaravel_vite_vue3に設定していますが任意の名前をつけてください。


 % laravel new laravel_vite_vue3

composerコマンドを利用してもプロジェクトを作成することができます。


 % composer create-project laravel/laravel laravel_vite_vue3

Viteの確認

コマンド実行後に作成されるプロジェクトフォルダに移動するとvite.config.jsファイルが確認できます。vite.config.jsファイルではViteを利用するために必要なlaravel-vite-pluginの設定が行われています。


import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [
        laravel({
            input: ['resources/css/app.css', 'resources/js/app.js'],
            refresh: true,
        }),
    ],
});

inputファイルとしてresourcecs/css/app.cssとresources/js/app.jsファイルが指定されています。app.cssを見ると中身は空ですがapp.jsではbootstrap.jsファイルがimportされています。


import './bootstrap';

bootstrat.jsにはloadshやaxiosのimportなどこれまでのLaravelのバージョンでも利用されてきたコードが記述されています。


import _ from 'lodash';
window._ = _;

/**
 * We'll load the axios HTTP library which allows us to easily issue requests
 * to our Laravel back-end. This library automatically handles sending the
 * CSRF token as a header based on the value of the "XSRF" token cookie.
 */

import axios from 'axios';
window.axios = axios;

window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

/**
 * Echo exposes an expressive API for subscribing to channels and listening
 * for events that are broadcast by Laravel. Echo and event broadcasting
 * allows your team to easily build robust real-time web applications.
 */

// import Echo from 'laravel-echo';

// import Pusher from 'pusher-js';
// window.Pusher = Pusher;

// window.Echo = new Echo({
//     broadcaster: 'pusher',
//     key: import.meta.env.VITE_PUSHER_APP_KEY,
//     wsHost: import.meta.env.VITE_PUSHER_HOST ?? `ws-${import.meta.env.VITE_PUSHER_APP_CLUSTER}.pusher.com`,
//     wsPort: import.meta.env.VITE_PUSHER_PORT ?? 80,
//     wssPort: import.meta.env.VITE_PUSHER_PORT ?? 443,
//     forceTLS: (import.meta.env.VITE_PUSHER_SCHEME ?? 'https') === 'https',
//     enabledTransports: ['ws', 'wss'],
// });

またpackage.jsonファイルを見るとviteが含まれていることもわかります。scriptsを見るとviteとvite buildも設定されています。


{
    "private": true,
    "scripts": {
        "dev": "vite",
        "build": "vite build"
    },
    "devDependencies": {
        "axios": "^0.27",
        "laravel-vite-plugin": "^0.6.0",
        "lodash": "^4.17.19",
        "postcss": "^8.1.14",
        "vite": "^3.0.0"
    }
}

Viteの設定

確認してきた通りViteはデフォルトから利用できる設定になっていますがVue.jsを利用するためにはいくつか設定が必要となります。

まずViteでVue.jsを利用するために@vitejs/plugin-vueのインストールを行います。


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

@vitejs/plugin-vueのインストールが完了したらvite.config.jsにvueを利用するための追加設定を行います。


import { defineConfig } from "vite";
import laravel from "laravel-vite-plugin";
import vue from "@vitejs/plugin-vue";

export default defineConfig({
    plugins: [
        vue(),
        laravel({
            input: ["resources/css/app.css", "resources/js/app.js"],
            refresh: true,
        }),
    ],
});

npm installコマンドを実行してJavaScriptのパッケージのインストールを行います。npm run devコマンドを実行するとviteの開発サーバがhttp://127.0.0.1:5174/で起動します。viteの開発サーバはファイルの更新を検知してブラウザ上に即座に変更を反映してくれます。この起動した開発サーバのURLについては後ほどどこで利用されるか確認することができます。


% npm run dev

> dev
> vite

Port 5173 is in use, trying another one...

  VITE v3.1.1  ready in 597 ms

  ➜  Local:   http://127.0.0.1:5174/
  ➜  Network: use --host to expose

  LARAVEL v9.30.1  plugin v0.6.0

  ➜  APP_URL: http://laravel_vite_vue3.test

app.jsファイルの読み込み

Vue.jsを利用するためにwelcome.blade.phpファイルでJavaScriptを読み込む必要があります。またVue.jsを利用するためにはマウントポイントが必要となります。JavaScriptの読み込みは@vite Directiveを利用します。マウントポイントはdiv要素のid属性にappを設定します。設定後のwelcome.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>Laravel Vite React</title>
        @vite(['resources/css/app.css', 'resources/js/app.js'])
    </head>
    <body>
        <div id="app"></div>
    </body>
</html>

php artisan serveコマンドを実行してLaravelの開発サーバを起動します。


 % php artisan serve

   INFO  Server running on [http://127.0.0.1:8000].  

  Press Ctrl+C to stop the server

ブラウザ上には何も表示されませんがブラウザのデベロッパーツールの要素を確認します。先ほどViteを起動した時に確認したポート番号でapp.cssとapp.jsファイルにアクセスしていることがわかります。

welcome.blade.phpファイルの要素の確認
welcome.blade.phpファイルの要素の確認

@viteディレクティブによってapp.jsファイルが読み込まれることがわかったのでapp.jsファイルからimportされているbootstrap.jsファイルでVue.jsのコードを記述します。


import _ from "lodash";
window._ = _;

/**
 * We'll load the axios HTTP library which allows us to easily issue requests
 * to our Laravel back-end. This library automatically handles sending the
 * CSRF token as a header based on the value of the "XSRF" token cookie.
 */

import axios from "axios";
window.axios = axios;

window.axios.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest";

import { createApp } from "vue";

const app = createApp();

console.log(app.version);

app.mount("#app");

//略
bootstrap.jsファイルではなくapp.jsファイルにVue.jsを記述しても問題ありません。

動作確認のためcreateAppでVueのインスタンスを作成しapp.versionでVue.jsのバージョンを確認しています。app.mountではVueをマウントする場所を指定しています。これはwelcome.blade.phpファイルで設定したidにappを持つdiv要素です。

npm run devコマンドを実行している場合は更新した内容が自動でビルドされるのでLaravelの開発サーバのhttp://localhost:8000にアクセスします。

ブラウザのコンソールには”[Vue warn]: Component is missing template or render function.”の警告メッセージが表示されていますがVueのバージョンも表示されていることが確認できます。本文書の作成時ではVue.jsの最新版の3.2.39と表示されています。

Vue.jsのバージョンの確認
Vue.jsのバージョンの確認

createAppの引数にルートコンポーネントを設定するためApp.vueファイルを作成します。


<template>
    <h1>Hello World</h1>
</template>

作成したApp.vueファイルをimportしてcreateAppの引数に設定します。


//略
import { createApp } from "vue";
import App from "./App.vue";

const app = createApp(App);

app.mount("#app");
//略

ブラウザでlocalhost:8000にアクセスするとApp.vueファイルで設定した”Hello World”が表示されます。

Hello Worldの表示
Hello Worldの表示

App.vueファイルのコードを変更すると変更した内容は自動で反映されます。app.cssファイルにスタイルの設定を追加しても自動でブラウザに反映されます。


h1 {
    text-decoration: underline;
}
CSSファイルによるスタイルの適用
CSSファイルによるスタイルの適用

npm run devを起動していない場合はどうなるかを確認するとviteの開発サーバが起動していない場ので下記のエラー画面が表示されます。Vue.jsを利用して開発を進める際にはnpm run devを必ず実行しておく必要があります。

npm run devコマンドを実行していない場合のエラー
npm run devコマンドを実行していない場合のエラー

コンポーネントの作成

Composition APIを利用してCounterコンポーネントを作成してVue.jsが問題なく動作するか確認を行います。jsフォルダの下にcomonentsフォルダを作成し、Counter.vueファイルを作成します。


<script setup>
import { ref } from "vue";

const count = ref(0);

const addCount = () => {
    count.value = count.value + 1;
};
</script>

<template>
    <h2>Counter</h2>
    <p>Count:{{ count }}</p>
    <button @click="addCount">Add</button>
</template>

作成したCounter.vueファイルをApp.vueファイルでimortします。


<script setup>
import Counter from "./components/Counter.vue";
</script>

<template>
    <h1>Hello World</h1>
    <Counter />
</template>

作成後ブラウザを確認するとカウンターが表示され”Add”ボタンをクリックするとカウント数が上がることが確認できます。

カウンターコンポーネントの動作確認
カウンターコンポーネントの動作確認

LaravelのVite環境でVue.jsのコンポーネントを使える環境を構築することができました。

ビルドコマンドの実行

npm run devコマンドはなく本番用のnpm run buildコマンドを実行します。publicフォルダの中にbuildフォルダが作成されmanifest.jsonファイル、さらにcssとJavaScriptファイルが作成されビルドされたファイルが保存されます。


 % npm run build

> build
> vite build

vite v3.1.1 building for production...
✓ 64 modules transformed.
public/build/manifest.json             0.25 KiB
public/build/assets/app.6fe91e6c.css   0.03 KiB / gzip: 0.05 KiB
public/build/assets/app.d146be1b.js 

php artisan serveコマンドでLaravelの開発サーバを起動します。ブラウザのデベロッパーツールの要素を確認します。先ほどまではnpm run devコマンドを実行時のhttp://127.0.0.1:5174/ではなくpubicフォルダのassetsフォルダのビルド後のファイルが設定されていることがわかります。

npm run buildコマンド実行後
npm run buildコマンド実行後

npm run devを実行するとpublicフォルダにhotという名前のファイルが作成されViteの開発サーバのURLが記述されています。npm run devを終了すると消えます。npm run buildの時はhotファイルが作成されることはなくこのファイルが存在することで開発環境か本番環境かを判断しているようです。

画像の表示

Vue.jsのコンポーネントで画像を表示する方法を確認していきます。

Appコンポーネントに画像を設定するためにresourcesフォルダの下にimagesフォルダを作成してlog-with-shadow.pngを保存します。log-with-shadow.pngファイルはViteのホームページのトップに表示されている画像を借りました。

画像はimportして利用することができます。その場合はv-bindを利用して下記のように設定することができます。


<script setup>
import Counter from "@/components/Counter.vue";
import logo from "../images/logo-with-shadow.png";
</script>

<template>
    <h1>Hello World!</h1>
    <img :src="logo" />
    <Counter />
</template>
importした画像の表示
importした画像の表示

importではなくimgのsrc属性にパスの設定でも画像は表示することができます。


<script setup>
import Counter from "@/components/Counter.vue";
</script>

<template>
    <h1>Hello World!</h1>
    <img src="../images/logo-with-shadow.png" />
    <Counter />
</template>

importまたはパスを設定した状態でビルドを行ってみてください。resource/imagesの下に保存したファイルはビルドを行うとpublic/build/assets/の下に保存されることがわかります。


% npm run build

> build
> vite build

vite v3.1.1 building for production...
✓ 66 modules transformed.
public/build/assets/logo-with-shadow.51249ca9.png   65.58 KiB
public/build/manifest.json                          0.46 KiB
public/build/assets/app.6fe91e6c.css                0.03 KiB / gzip: 0.05 KiB
public/build/assets/app.10b97486.js  

ブラウザ上でパスを確認すると/build/assets/logo-with-shadow.51249ca9.pngになっていることがわかります。

publicフォルダの下にファイルを保存すると下記のように設定することができます。


<img src="/logo-with-shadow.png" />

npm run devコマンドを実行すると上記の設定でも画像が表示されますがnpm run buildを設定するとエラーが発生します。


 % npm run build

> build
> vite build

vite v3.1.1 building for production...
✓ 9 modules transformed.
[vite]: Rollup failed to resolve import "/logo-with-shadow.png" from "resources/js/App.vue".
This is most likely unintended because it can break your application at runtime.
If you do want to externalize this module explicitly add it to
`build.rollupOptions.external`
error during build:
Error: [vite]: Rollup failed to resolve import "/logo-with-shadow.png" from "resources/js/App.vue".
This is most likely unintended because it can break your application at runtime.
//略

この問題を解消するためにはLaravelのドキュメントに記載されている内容に従ってvite.config.jsファイルを更新します。


import { defineConfig } from "vite";
import laravel from "laravel-vite-plugin";
import vue from "@vitejs/plugin-vue";

export default defineConfig({
    plugins: [
        vue({
            template: {
                transformAssetUrls: {
                    base: null,
                    includeAbsolute: false,
                },
            },
        }),
        laravel({
            input: ["resources/css/app.css", "resources/js/app.js"],
            refresh: true,
        }),
    ],
});

vite.config.jsファイルを更新後にnpm run buildを実行するとビルドは成功します。メッセージを見ると先程の場合は画像はpublic/build/assetsの中に作成されていましたが今回の場合は作成されません。


 % npm run build

> build
> vite build

vite v3.1.1 building for production...
✓ 64 modules transformed.
public/build/manifest.json             0.25 KiB
public/build/assets/app.6fe91e6c.css   0.03 KiB / gzip: 0.05 KiB
public/build/assets/app.02c50ad9.js    142.80 KiB / gzip: 53.68 KiB

最後にpublicの下に画像を保存したままパスの設定を変更します。


<img src="../../public/logo-with-shadow.png" />

vite.config.jsファイルの更新を行わなくてもビルドは動作しますがpublic/build/assetsの下にファイルが作成されます。

同じ場所に保存したも画像のパスを相対パスに設定するか絶対パスで設定するかによって追加の設定が必要となり、ビルドの結果も変わります。