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

# Laravel Wayfinder 介紹

> 介紹在 Inertia starter kit 中取代 Ziggy 而被採用的套件 Laravel Wayfinder。從以型別安全連接 Laravel 後端與 TypeScript 前端的機制、與 Ziggy 的差異、安裝與基本用法，到 next 分支中演進的下一代功能，一次深入介紹。

## 什麼是 Wayfinder

**Laravel Wayfinder** 是可將 Laravel 後端與 TypeScript 前端零阻力地連接的套件。它會自動從 controller 與路由產生完整型別化的 TypeScript 函式，因此可以從前端程式碼將 Laravel 的 endpoint 當作函式直接呼叫。

URL 硬編碼、路由參數的手動管理、後端變更的手動同步——這些全部都不再需要。

<Info>
  Wayfinder 為 Beta 版（目前 v0.1.x）。到 v1.0.0 釋出前 API 可能會變更。所有重要變更都會記錄在 [CHANGELOG](https://github.com/laravel/wayfinder/blob/main/CHANGELOG.md) 中。
</Info>

***

## Ziggy 與 Wayfinder 的差異

### 什麼是 Ziggy

[Ziggy](https://github.com/tighten/ziggy) 是長年廣泛用於 Laravel 生態系的路由 helper。它會將 Laravel 的路由定義公開到 JavaScript 端，可用 `route('posts.show', { id: 1 })` 這樣的形式產生 URL。

### 為何被 Wayfinder 取代

Ziggy 以字串處理路由名稱與參數，因此在與 TypeScript 相容性上有其極限。路由名稱的拼寫錯誤或錯誤的參數名只會變成執行時錯誤。

Wayfinder 是 TypeScript 優先設計，會將 controller 方法產生為 **可 import 的函式**。

| 比較項目         | Ziggy                            | Wayfinder                              |
| ------------ | -------------------------------- | -------------------------------------- |
| 路由參照方式       | `route('posts.show', { id: 1 })` | `import { show } from "@/actions/..."` |
| 型別安全         | 型別定義有限                           | 完整的 TypeScript 型別                      |
| IDE 支援       | 自動完成弱                            | 完全支援自動完成、型別檢查                          |
| Tree shaking | 所有路由都包含在 bundle                  | 只有使用的路由會進 bundle                       |
| 產生時機         | 執行時注入                            | 建置時靜態產生                                |

在基於 Inertia 的 Laravel 入門套件（React、Vue、Svelte）中，Wayfinder 是標準採用。

***

## 安裝

### 1. 使用 Composer 安裝伺服器端套件

```bash theme={null}
composer require laravel/wayfinder
```

### 2. 使用 NPM 安裝 Vite plugin

```bash theme={null}
npm i -D @laravel/vite-plugin-wayfinder
```

### 3. 在 `vite.config.js` 中新增 plugin

```ts theme={null}
import { wayfinder } from "@laravel/vite-plugin-wayfinder";
import { defineConfig } from "vite";
import laravel from "laravel-vite-plugin";

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

加入 Vite plugin 後，在開發伺服器執行期間，每當 PHP 檔案或路由檔案變更，就會自動重新產生 TypeScript 檔案。

***

## 產生 TypeScript 定義檔

使用 `wayfinder:generate` 指令產生 TypeScript 檔案。

```bash theme={null}
php artisan wayfinder:generate
```

預設會在 `resources/js` 下產生 3 個目錄。

```
resources/js/
├── actions/         # controller action 的函式
│   └── App/Http/Controllers/
│       └── PostController.ts
├── routes/          # 命名路由的函式
│   └── post.ts
└── wayfinder/       # 型別定義檔
    └── types.ts
```

<Tip>
  產生的檔案在每次 build 時都會完整重新產生，因此建議加入 `.gitignore`。請將 `wayfinder`、`actions`、`routes` 三個目錄一併排除。
</Tip>

若要變更輸出位置可使用 `--path` 選項。

```bash theme={null}
php artisan wayfinder:generate --path=resources/js/api
```

也可只產生 controller action 或只產生路由。

```bash theme={null}
php artisan wayfinder:generate --skip-actions  # 只產生路由
php artisan wayfinder:generate --skip-routes   # 只產生 action
```

***

## 基本用法

### import 與使用 action

以下是產生對應 `PostController` 的 `show` 方法的 URL 範例。

```ts theme={null}
import { show } from "@/actions/App/Http/Controllers/PostController";

show(1);
// { url: "/posts/1", method: "get" }
```

只需要 URL 時使用 `.url()`。

```ts theme={null}
show.url(1); // "/posts/1"
```

也可以指定特定的 HTTP method。

```ts theme={null}
show.head(1); // { url: "/posts/1", method: "head" }
```

### 傳遞參數的方式

Wayfinder 的函式可接受多種形式的參數。

```ts theme={null}
import { update } from "@/actions/App/Http/Controllers/PostController";

// 單一參數
show(1);
show({ id: 1 });

// 多重參數
update([1, 2]);
update({ post: 1, author: 2 });
update({ post: { id: 1 }, author: { id: 2 } });
```

若路由有指定 key binding（`/posts/{post:slug}`），可使用該值。

```ts theme={null}
// 若路由為 /posts/{post:slug}
show("my-new-post");
show({ slug: "my-new-post" });
```

### import 整個 controller

也可以 import 整個 controller 呼叫方法。

```ts theme={null}
import PostController from "@/actions/App/Http/Controllers/PostController";

PostController.show(1);
PostController.index();
```

<Warning>
  import 整個 controller 時 tree shaking 會失效，所有 action 都會包含在 bundle 中。個別 import 能讓最終 bundle 尺寸更小。
</Warning>

### 單一 action controller

單一 action controller（Invokable Controller）可以直接呼叫 import 的函式。

```ts theme={null}
import StorePostController from "@/actions/App/Http/Controllers/StorePostController";

StorePostController(); // { url: "/posts", method: "post" }
```

### import 命名路由

要以路由名稱存取時，請使用 `routes/` 下的檔案。

```ts theme={null}
import { show } from "@/routes/post";

// 若路由名稱為 `post.show`
show(1); // { url: "/posts/1", method: "get" }
```

### Query 參數

所有 Wayfinder 函式都可以用 `query` 選項加上 query 參數。

```ts theme={null}
import { show } from "@/actions/App/Http/Controllers/PostController";

show(1, { query: { page: 1, sort_by: "name" } });
// { url: "/posts/1?page=1&sort_by=name", method: "get" }
```

要與目前 URL 的 query 參數合併時使用 `mergeQuery`。

```ts theme={null}
// 目前 URL: /posts/1?page=1&sort_by=category&q=shirt

show.url(1, { mergeQuery: { page: 2, sort_by: "name" } });
// "/posts/1?page=2&sort_by=name&q=shirt"

// 要刪除參數時指定為 null
show.url(1, { mergeQuery: { sort_by: null } });
// "/posts/1?page=1&q=shirt"
```

### Form 變體

要在傳統 HTML form 中使用時，加上 `--with-form` 選項產生，並使用 `.form` 變體。

```bash theme={null}
php artisan wayfinder:generate --with-form
```

```tsx theme={null}
import { store, update } from "@/actions/App/Http/Controllers/PostController";

// React 範例
const Page = () => (
    <form {...store.form()}>
        {/* <form action="/posts" method="post"> */}
    </form>
);

const EditPage = () => (
    <form {...update.form(1)}>
        {/* <form action="/posts/1?_method=PATCH" method="post"> */}
    </form>
);
```

***

## Inertia 與 Wayfinder 的組合

結合 Inertia 的 form helper 與 Wayfinder，可以完全不寫 URL 字串即可送出表單。

```ts theme={null}
import { useForm } from "@inertiajs/react";
import { store } from "@/actions/App/Http/Controllers/PostController";

const form = useForm({ name: "My Post" });

form.submit(store()); // 送到 POST /posts
```

`Link` 元件也可以同樣使用。

```tsx theme={null}
import { Link } from "@inertiajs/react";
import { show } from "@/actions/App/Http/Controllers/PostController";

const Nav = () => (
    <Link href={show(1)}>查看貼文</Link>
);
```

***

## 於入門套件中的採用

使用 `laravel new` 建立新專案並選擇 React、Vue、Svelte 時，會取得已自動設定 Wayfinder 的組合。入門套件包含：

* Composer 套件 `laravel/wayfinder`
* NPM 套件 `@laravel/vite-plugin-wayfinder`
* `vite.config.js` 已設定 plugin
* `.gitignore` 已加入產生目錄

既有專案的手動導入也可依上述步驟進行。

***

## 保留字與衝突方法名稱的處理

`delete` 或 `import` 這類與 JavaScript 保留字同名的 controller 方法會加上 `Method` 後綴。

```ts theme={null}
// 若 controller 有 delete 方法
import { deleteMethod } from "@/actions/App/Http/Controllers/PostController";

deleteMethod(1); // { url: "/posts/1", method: "delete" }
```

***

## 目前狀況（v0.1.x）

目前穩定版於 `v0.1.x` 分支提供。截至 2026 年 3 月，最新版本為 **v0.1.15**。

### v0.1.x 系列主要變更歷程

| 版本      | 主要內容                               |
| ------- | ---------------------------------- |
| v0.1.15 | 支援 Laravel 13、修正 Blade view crash  |
| v0.1.13 | 改善 query 參數的 TypeScript strict 相容性 |
| v0.1.7  | 支援於前端指定 URL 預設參數                   |
| v0.1.6  | 新增 Vite plugin                     |
| v0.1.5  | 支援 PHP 8.2、支援已快取路由                 |
| v0.1.0  | 初始釋出                               |

***

## next 分支開發中的下一代功能

`next` 分支正在開發從目前 v0.1.x 大幅擴充功能的下一版本。

<Warning>
  `next` 分支可以用 `dev-next` 限制安裝，但 API 可能有大幅變更。不建議在正式環境使用。
</Warning>

```bash theme={null}
composer require laravel/wayfinder:dev-next
```

### 產生的 TypeScript 範圍大幅擴大

v0.1.x 只以路由與 controller action 為對象，下一版本則會將以下項目全部產生為 TypeScript。

```mermaid theme={null}
graph TD
    A["Laravel 應用"] --> B["wayfinder:generate"]
    B --> C["Routes & Actions<br>路由 URL 函式"]
    B --> D["Form Requests<br>驗證型別"]
    B --> E["Eloquent Models<br>模型介面"]
    B --> F["PHP Enums<br>TypeScript 常數"]
    B --> G["Inertia Page Props<br>頁面 prop 型別"]
    B --> H["Broadcast Channels<br>channel 型別"]
    B --> I["Broadcast Events<br>event payload 型別"]
    B --> J["Environment Variables<br>import.meta.env 型別"]
```

### Form Request 的 TypeScript 型別產生

```php theme={null}
class StorePostRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'title'   => ['required', 'string', 'max:255'],
            'content' => ['required', 'string'],
            'tags'    => ['nullable', 'array'],
            'tags.*'  => ['string'],
        ];
    }
}
```

上述 Form Request 會產生以下型別。

```ts theme={null}
export type Request = {
    title: string;
    content: string;
    tags?: string[] | null;
};
```

### Eloquent Model 的型別產生

```php theme={null}
class User extends Model
{
    protected $casts = [
        'email_verified_at' => 'datetime',
        'is_admin' => 'boolean',
    ];

    public function posts(): HasMany
    {
        return $this->hasMany(Post::class);
    }
}
```

上述模型會在 `types.d.ts` 中產生型別。

```ts theme={null}
export namespace App.Models {
    export type User = {
        id: number;
        name: string;
        email: string;
        email_verified_at: string | null;
        is_admin: boolean;
        posts: App.Models.Post[];
    };
}
```

### PHP Enum 轉為 TypeScript

```php theme={null}
enum PostStatus: string
{
    case Draft = 'draft';
    case Published = 'published';
    case Archived = 'archived';
}
```

型別與常數會同時產生。

```ts theme={null}
// 型別定義（types.d.ts）
export namespace App.Enums {
    export type PostStatus = "draft" | "published" | "archived";
}

// 常數（App/Enums/PostStatus.ts）
export const Draft = "draft";
export const Published = "published";
export const Archived = "archived";

export const PostStatus = { Draft, Published, Archived } as const;
```

### 輸出目錄的變更

v0.1.x 分為 `actions/`、`routes/`、`wayfinder/` 三個目錄，下一版本將統整到 `resources/js/wayfinder` 下。

```
resources/js/wayfinder/
├── App/Http/Controllers/
│   └── PostController.ts    # action 函式（廢除 actions/）
├── routes/
│   └── post.ts              # 命名路由（相同）
├── broadcast-channels.ts    # broadcast channel
├── broadcast-events.ts      # broadcast event
└── types.d.ts               # 所有型別定義（從 types.ts 變更）
```

### 從 v0.1.x 到 next 的主要變更

* import 路徑從 `@/actions/...` 變更為 `@/wayfinder/...`
* 廢除 `--skip-actions`、`--skip-routes`、`--with-form` flag，改為透過設定檔設定
* `types.ts` 改為 `types.d.ts`

***

## 總結

Laravel Wayfinder 是將 Ziggy 提供的「從 JavaScript 參照 Laravel 路由」功能以 TypeScript 優先重新設計的套件。透過 import 產生函式後使用的方式，在型別安全、IDE 支援、tree shaking 三個面向都大幅改善。

目前的 v0.1.x 也能實現型別安全地參照路由與 controller action，並在基於 Inertia 的 Laravel 入門套件中作為標準採用。而正在 `next` 分支開發的下一版本，將進化為包含 Form Request、Eloquent Model、Enum、Inertia 頁面 prop 都以 TypeScript 產生的更全面型別安全基盤。

<Card title="Laravel Wayfinder GitHub" icon="github" href="https://github.com/laravel/wayfinder">
  原始碼、CHANGELOG、Issue 請見此處。
</Card>

<Card title="Vite Plugin Wayfinder" icon="bolt" href="https://github.com/laravel/vite-plugin-wayfinder">
  Vite plugin 設定選項的詳情請見此處。
</Card>


## Related topics

- [部落格](/zh-TW/blog/index.md)
- [Blaze 套件介紹](/zh-TW/blog/blaze-introduction.md)
- [Laravel Pennant 實務案例](/zh-TW/blog/laravel-pennant.md)
- [Laravel Telescope 實戰技巧](/zh-TW/blog/telescope-introduction.md)
- [用 Pest PHP 開始 Laravel 測試](/zh-TW/blog/pest-introduction.md)
