> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Vue.js 入門 — 搭配 Inertia × Laravel 使用的基礎知識

> 介紹 Laravel 使用者最熟悉的 JS 框架 Vue.js。從 Options API / Composition API 的概觀，到 Inertia v3 × Vue 3 的頁面元件、useForm、共享資料，實用地一次講解。

## 什麼是 Vue.js

Vue.js（以下簡稱 Vue）是用於建構使用者介面的漸進式 JavaScript 框架。「漸進式」意指可以從小規模開始，並依需要追加功能，既能部分嵌入既有 HTML 頁面，也能建構大規模的 SPA。

Vue 的核心是**反應式**。當資料變化時 DOM 會自動更新，因此開發者不需要手動管理「何時、更新哪個元素」。

<Info>
  本頁介紹的是 Vue 3 與 Inertia v3 的組合。Laravel 13 的入門套件預設使用此組合。
</Info>

### Options API 與 Composition API

Vue 3 提供了兩種撰寫元件的風格：**Options API** 與 **Composition API**。

**Options API** 是延續 Vue 2 的傳統風格。以 `data`、`methods`、`computed`、`mounted` 等選項物件定義元件。

```vue theme={null}
<!-- Options API 範例 -->
<script>
export default {
    data() {
        return { count: 0 }
    },
    methods: {
        increment() {
            this.count++
        }
    }
}
</script>

<template>
    <button @click="increment">{{ count }}</button>
</template>
```

**Composition API** 是 Vue 3 導入的新風格。結合 `<script setup>` 語法後可以更簡潔地撰寫。邏輯的可重用性也更高，與 TypeScript 相容性也很好。

```vue theme={null}
<!-- Composition API（<script setup>）範例 -->
<script setup>
import { ref } from 'vue'

const count = ref(0)

function increment() {
    count.value++
}
</script>

<template>
    <button @click="increment">{{ count }}</button>
</template>
```

<Tip>
  Inertia × Laravel 的入門套件標準採用 `<script setup>` 的 Composition API 風格。本頁範例也全部以 `<script setup>` 撰寫。
</Tip>

***

## 在 Laravel 中的定位

### 歷史

Vue 與 Laravel 的關係歷史悠久，可追溯至 **Laravel 5.3（2016 年）** 採用 Vue 作為預設前端框架。當時的 `package.json` 中就包含 Vue，並附有 `resources/js/components/ExampleComponent.vue` 這樣的範例元件。

```mermaid theme={null}
timeline
    title Laravel 與 Vue 的軌跡
    2016 : Laravel 5.3 — 預設採用 Vue
    2019 : Laravel 6 — 將 Vue scaffold 分離到 laravel/ui 套件
    2021 : Laravel 8 — 出現 Jetstream + Inertia (Vue) 入門套件
    2022 : Laravel 9 — 移轉到 Vite
    2025 : Laravel 12 — 更新入門套件（Vue / React）
    2026 : Laravel 13 — 支援 Inertia v3 的入門套件
```

**Laravel 6（2019 年）** 將認證 scaffold 切分為 `laravel/ui` 套件，Vue 的 scaffold 也移至該套件。目前主流是透過 `laravel new` 的入門套件選擇 Inertia + Vue 組合。

對 Laravel 使用者來說，Vue 是最熟悉的 JS 框架，日文學習資源也很豐富。

### 目前的主流方式：Inertia × Vue

目前 Laravel 中使用 Vue 的主要方式是 **Inertia × Vue**。Inertia 可以不設計 API，直接從 Laravel 的 controller 將資料傳給 Vue 元件，實現「現代單體式」架構。

```mermaid theme={null}
graph LR
    Browser["瀏覽器"]
    Inertia["Inertia.js<br>（Adapter 層）"]
    Laravel["Laravel<br>（Controller）"]
    Vue["Vue<br>（頁面元件）"]

    Browser <-->|XHR / 整頁載入| Inertia
    Inertia <-->|Inertia 回應| Laravel
    Inertia -->|props| Vue
    Vue -->|渲染| Browser
```

***

## 安裝設定

### 透過入門套件（推薦）

要新建專案時，使用入門套件是最方便的方式。

```shell theme={null}
laravel new my-app
```

在互動式提示中選擇 **Vue**，以下項目就會全部自動設定完成。

* `inertiajs/inertia-laravel`（伺服器端 adapter）
* `@inertiajs/vue3`（客戶端 adapter）
* `vue`（Vue 3 主體）
* `@vitejs/plugin-vue`（Vite plugin）
* `HandleInertiaRequests` middleware
* 登入、註冊等認證畫面（以 Inertia + Vue 實作完畢）

### 手動安裝

要加入既有專案時，需將伺服器端與客戶端分開安裝。

```shell theme={null}
# 伺服器端（PHP）
composer require inertiajs/inertia-laravel

# 客戶端（JavaScript）
npm install @inertiajs/vue3 vue
npm install --save-dev @vitejs/plugin-vue
```

接著，在 `vite.config.js` 中加入 Vue plugin。

```js theme={null}
import { defineConfig } from 'vite'
import laravel from 'laravel-vite-plugin'
import vue from '@vitejs/plugin-vue'

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

在 `resources/js/app.js` 啟動 Inertia 應用。

```js theme={null}
import { createApp, h } from 'vue'
import { createInertiaApp } from '@inertiajs/vue3'
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'

createInertiaApp({
    resolve: (name) =>
        resolvePageComponent(
            `./pages/${name}.vue`,
            import.meta.glob('./pages/**/*.vue'),
        ),
    setup({ el, App, props, plugin }) {
        createApp({ render: () => h(App, props) })
            .use(plugin)
            .mount(el)
    },
})
```

<Info>
  手動安裝的詳情（root template 設定、middleware 註冊等）請參考 [Inertia 官方文件](https://inertiajs.com/installation)。
</Info>

***

## 目錄結構

入門套件將 Vue 的頁面元件放在 `resources/js/pages/` 目錄下。

```
resources/js/
├── app.js             # Inertia 應用的起點
├── bootstrap.js
├── components/        # 可重用的 UI 元件
│   ├── NavBar.vue
│   └── ...
├── layouts/           # 版面配置元件
│   ├── AppLayout.vue
│   └── AuthLayout.vue
└── pages/             # Inertia 頁面元件（對應 controller 名稱）
    ├── Auth/
    │   ├── Login.vue
    │   └── Register.vue
    ├── Dashboard.vue
    └── Posts/
        ├── Index.vue
        ├── Create.vue
        └── Show.vue
```

寫成 `Inertia::render('Posts/Index', [...])` 時，`resources/js/pages/Posts/Index.vue` 就會是對應的元件。

***

## Vue 模板語法

介紹讀寫入門套件程式碼所需的基本模板 directive。

#### `{{ }}` — 變數展開

使用雙大括號將 JavaScript 值或運算式內嵌到模板中。

```vue theme={null}
<script setup>
const name = '世界'
const count = 3
</script>

<template>
    <p>你好，{{ name }}！</p>
    <p>兩倍是 {{ count * 2 }}</p>
</template>
```

#### `v-if` — 條件分支

```vue theme={null}
<template>
    <p v-if="isLoggedIn">歡迎！</p>
    <p v-else-if="role === 'admin'">以管理員身份登入中</p>
    <a v-else href="/login">登入</a>
</template>
```

相當於 Svelte 的 `{#if}` 或 React 的三元運算子。

#### `v-for` — 清單渲染

```vue theme={null}
<template>
    <ul>
        <li v-for="post in posts" :key="post.id">{{ post.title }}</li>
    </ul>
</template>
```

為了高效的差異更新，`:key` 必須指定。相當於 React 的 `Array.map()`。

#### `v-model` — 雙向繫結

使用 `v-model` 可讓表單元素的值與反應式變數進行雙向同步。

```vue theme={null}
<script setup>
import { ref } from 'vue'

const title = ref('')
const agreed = ref(false)
const role = ref('viewer')
</script>

<template>
    <!-- 文字輸入 -->
    <input v-model="title" type="text" />
    <p>輸入中: {{ title }}</p>

    <!-- 核取方塊 -->
    <input v-model="agreed" type="checkbox" />
    <p>同意: {{ agreed }}</p>

    <!-- 下拉選單 -->
    <select v-model="role">
        <option value="viewer">閱覽者</option>
        <option value="editor">編輯者</option>
        <option value="admin">管理員</option>
    </select>
</template>
```

#### `:` (v-bind) 與 `@` (v-on)

* `:attr="value"` — 對 HTML 屬性動態繫結值（`v-bind:attr` 的簡寫）
* `@event="handler"` — 註冊事件監聽器（`v-on:event` 的簡寫）

```vue theme={null}
<template>
    <!-- 動態屬性繫結 -->
    <img :src="imageUrl" :alt="imageAlt" />

    <!-- 事件處理器 -->
    <button @click="handleClick">點選</button>
    <form @submit.prevent="handleSubmit">...</form>
</template>
```

***

## 頁面元件的基本

Inertia 的頁面元件就是一般的 Vue 元件。可將從 Laravel controller 傳入的資料作為 props 接收。

### Controller

```php theme={null}
// app/Http/Controllers/PostController.php
use Inertia\Inertia;
use App\Models\Post;

class PostController extends Controller
{
    public function index()
    {
        return Inertia::render('Posts/Index', [
            'posts' => Post::latest()->paginate(10),
        ]);
    }
}
```

### Vue 頁面元件

```vue theme={null}
<!-- resources/js/pages/Posts/Index.vue -->
<script setup>
import { Link } from '@inertiajs/vue3'

defineProps({
    posts: Object,
})
</script>

<template>
    <div>
        <h1>貼文列表</h1>
        <article v-for="post in posts.data" :key="post.id">
            <h2>
                <Link :href="`/posts/${post.id}`">{{ post.title }}</Link>
            </h2>
            <p>{{ post.created_at }}</p>
        </article>
    </div>
</template>
```

只要以 `defineProps()` 宣告 props，就能在模板中使用 controller 傳入的資料。不需要定義 REST API。

***

## `Link` 元件

使用 `@inertiajs/vue3` 提供的 `<Link>` 元件時，頁面切換以 XHR 進行，可避免瀏覽器整頁重新載入。

```vue theme={null}
<script setup>
import { Link } from '@inertiajs/vue3'
</script>

<template>
    <!-- 基本連結 -->
    <Link href="/posts">貼文列表</Link>

    <!-- 使用 POST 方法的連結（如刪除） -->
    <Link href="/posts/1" method="delete" as="button" type="button">
        刪除
    </Link>

    <!-- 預先載入（hover 時預取資料） -->
    <Link href="/posts/1" preload>查看貼文</Link>
</template>
```

寫法與一般 `<a>` 標籤相同，但背後 Inertia 只會替換頁面元件，帶來如 SPA 般的操作體驗。

***

## `Form` 元件

`@inertiajs/vue3` 提供的 `<Form>` 元件是入門套件認證畫面中所採用的表單送出推薦寫法。以 props 指定 `action` 與 `method`，用 `v-slot` 存取 `errors` 與 `processing`。

### 基本用法

```vue theme={null}
<script setup>
import { Form } from '@inertiajs/vue3'
</script>

<template>
    <Form action="/posts" method="post" class="flex flex-col gap-4" v-slot="{ errors, processing }">
        <div>
            <label for="title">標題</label>
            <input id="title" name="title" type="text" required />
            <p v-if="errors.title" class="error">{{ errors.title }}</p>
        </div>

        <div>
            <label for="content">內文</label>
            <textarea id="content" name="content"></textarea>
            <p v-if="errors.content" class="error">{{ errors.content }}</p>
        </div>

        <button type="submit" :disabled="processing">
            {{ processing ? '傳送中...' : '發布' }}
        </button>
    </Form>
</template>
```

`v-slot="{ errors, processing }"` 是 Vue 的 scoped slot 語法，`Form` 元件會自動計算這些值並傳入。表單欄位不使用 `v-model`，而是使用 HTML 原生的 `name` 屬性，讓瀏覽器標準的表單資料蒐集機制正常運作。

### 入門套件的模式

入門套件使用 [Wayfinder](/zh-TW/blog/wayfinder-introduction) 以物件形式管理路由。`store.form()` 會回傳包含路由物件 `action` 與 `method` 的物件，用 `v-bind` spread 到 `<Form>`。

```vue theme={null}
<script setup lang="ts">
import { Form } from '@inertiajs/vue3'
import { store } from '@/routes/login'
</script>

<template>
    <Form
        v-bind="store.form()"
        :reset-on-success="['password']"
        v-slot="{ errors, processing }"
        class="flex flex-col gap-6"
    >
        <!-- 表單內容 -->
    </Form>
</template>
```

指定為 `reset-on-success` 的欄位會在送出成功時自動重設。適用於密碼欄位等送出後想清空的欄位。

<Info>
  若不使用 Wayfinder，也可直接傳入 `action="/login"` 之類的 URL，效果相同。
</Info>

***

## `useForm` helper

表單處理使用 `@inertiajs/vue3` 的 `useForm` helper。可以簡潔地實作表單狀態管理、送出與驗證錯誤顯示。

### Controller 側

```php theme={null}
// app/Http/Controllers/PostController.php
class PostController extends Controller
{
    public function store(Request $request)
    {
        $validated = $request->validate([
            'title'   => ['required', 'string', 'max:255'],
            'content' => ['required', 'string'],
        ]);

        Post::create($validated + ['user_id' => auth()->id()]);

        return redirect()->route('posts.index')
            ->with('success', '貼文已建立。');
    }
}
```

### Vue 表單元件

```vue theme={null}
<!-- resources/js/pages/Posts/Create.vue -->
<script setup>
import { useForm } from '@inertiajs/vue3'

const form = useForm({
    title: '',
    content: '',
})

function submit() {
    form.post('/posts')
}
</script>

<template>
    <form @submit.prevent="submit">
        <div>
            <label>標題</label>
            <input v-model="form.title" type="text" />
            <p v-if="form.errors.title" class="error">{{ form.errors.title }}</p>
        </div>

        <div>
            <label>內文</label>
            <textarea v-model="form.content"></textarea>
            <p v-if="form.errors.content" class="error">{{ form.errors.content }}</p>
        </div>

        <button type="submit" :disabled="form.processing">
            {{ form.processing ? '傳送中...' : '發布' }}
        </button>
    </form>
</template>
```

以下整理 `useForm` 回傳物件的主要屬性。

| 屬性 / 方法            | 說明                  |
| ------------------ | ------------------- |
| `form.data`        | 表單資料物件              |
| `form.errors`      | 驗證錯誤（以欄位名存取）        |
| `form.processing`  | 傳送中為 `true`（用於禁用按鈕） |
| `form.isDirty`     | 若已從初始值變更則為 `true`   |
| `form.post(url)`   | 以 POST 請求送出         |
| `form.put(url)`    | 以 PUT 請求送出（更新）      |
| `form.delete(url)` | 以 DELETE 請求送出       |
| `form.reset()`     | 重設表單為初始值            |

當驗證錯誤返回時，`useForm` 會保留輸入內容並顯示錯誤。搭配 `v-model` 即可實現無縫的表單體驗。

***

## 共享資料（Shared Data）

所有頁面都需要的共通資料（登入使用者資訊、flash 訊息等），在 `HandleInertiaRequests` middleware 的 `share()` 方法中定義。

```php theme={null}
// app/Http/Middleware/HandleInertiaRequests.php
use Illuminate\Http\Request;
use Inertia\Middleware;

class HandleInertiaRequests extends Middleware
{
    public function share(Request $request): array
    {
        return array_merge(parent::share($request), [
            'auth' => [
                'user' => $request->user()
                    ? $request->user()->only('id', 'name', 'email')
                    : null,
            ],
            'flash' => [
                'success' => $request->session()->get('success'),
                'error'   => $request->session()->get('error'),
            ],
        ]);
    }
}
```

在 Vue 元件中透過 `usePage()` 存取共享資料。

```vue theme={null}
<script setup>
import { computed } from 'vue'
import { usePage } from '@inertiajs/vue3'

const page = usePage()

// 存取共享資料
const user = computed(() => page.props.auth.user)
const flash = computed(() => page.props.flash)
</script>

<template>
    <header>
        <span v-if="user">{{ user.name }}</span>
        <span v-else>訪客</span>
    </header>

    <div v-if="flash.success" class="alert-success">
        {{ flash.success }}
    </div>
</template>
```

<Info>
  共享資料會包含在所有請求中，因此建議只放最必要的資料。使用 `fn()` 進行 lazy 求值時，只在實際被存取時才會被求值。
</Info>

***

## Vue 3 反應式基礎

以下介紹以 Inertia × Vue 開發時，需要了解的 Vue 3 反應式 API。

### `ref` — 原始型反應式值

```vue theme={null}
<script setup>
import { ref } from 'vue'

const count = ref(0)
const isOpen = ref(false)

// 以 .value 存取（在模板中不需要）
count.value++
</script>

<template>
    <p>{{ count }}</p>
    <button @click="isOpen = !isOpen">切換</button>
</template>
```

### `computed` — 計算屬性

```vue theme={null}
<script setup>
import { ref, computed } from 'vue'

const posts = ref([])

const publishedPosts = computed(() =>
    posts.value.filter(post => post.published)
)
</script>
```

### `onMounted` — 掛載後處理

```vue theme={null}
<script setup>
import { onMounted } from 'vue'

onMounted(() => {
    console.log('元件已掛載')
})
</script>
```

***

## 總結

Vue.js 與 Laravel 相容性極佳，特別是透過 Inertia 的「現代單體式」架構下能發揮實力。

| 元素                  | 角色                       |
| ------------------- | ------------------------ |
| Laravel Controller  | 路由、資料取得、驗證               |
| `Inertia::render()` | 從 Controller 傳資料到 Vue 元件 |
| Vue 頁面元件            | 接收 props 並渲染 UI          |
| `useForm`           | 表單狀態管理、送出、錯誤顯示           |
| `Link` 元件           | 不重新整頁的頁面切換               |
| `usePage().props`   | 存取共享資料                   |

使用 Inertia × Vue 可同時享有 Laravel 後端的簡潔性與 Vue 反應式 UI 兩者的優點。使用入門套件建立專案時，包含認證畫面在內都能立即開始開發。

<Card title="Inertia.js 官方文件" icon="book-open" href="https://inertiajs.com">
  Inertia v3 完整功能請參考官方文件。
</Card>


## Related topics

- [React 入門 — 搭配 Inertia × Laravel 使用的基礎知識](/zh-TW/blog/react-introduction.md)
- [Svelte 入門 — 搭配 Inertia × Laravel 使用的基礎知識](/zh-TW/blog/svelte-introduction.md)
- [認證入門](/zh-TW/authentication.md)
- [開始學習 Laravel 前需要具備的知識](/zh-TW/true-tutorial.md)
- [前端](/zh-TW/frontend.md)
