---
# System prepended metadata

title: Livewire Volt 中文文檔
tags: [Laravel]

---

Volt 是一個精心設計的 Livewire 函式 API，它支援單一檔案元件，允許元件的 PHP 邏輯和 Blade 模板共存於同一個檔案中。在內部運作上，該函式 API 會編譯成 Livewire 類別元件，並與同一檔案中的模板連結。

一個簡單的 Volt 元件範例如下：

```php
<?php
 
use function Livewire\Volt\{state};
 
state(['count' => 0]);
 
$increment = fn () => $this->count++;
 
?>
 
<div>
    <h1>{{ $count }}</h1>
    <button wire:click="increment">+</button>
</div>
```
# API 風格
利用 Volt 的函式 API，我們可以透過引入 `Livewire\Volt` 中的函式來定義 Livewire 元件的邏輯。Volt 隨後會將這些函式程式碼轉換並編譯成傳統的 Livewire 類別，讓我們可以利用 Livewire 的強大功能，同時減少樣板程式碼。

Volt 的 API 會自動將任何閉包綁定到底層的元件上。因此，在任何時間，動作、計算屬性或監聽器皆可以使用 `$this` 變數來參考元件：

```php
use function Livewire\Volt\{state};
 
state(['count' => 0]);
 
$increment = fn () => $this->count++;
 
// ...
```

# 渲染與掛載元件
與典型的 Livewire 元件一樣，Volt 元件也可以使用 Livewire 的標籤語法或 `@livewire` Blade 指令進行渲染：

```blade
<livewire:user-index :users="$users" />
```

要聲明元件所接受的屬性，可以使用 `state` 函式：

```php
use function Livewire\Volt\{state};
 
state('users');
 
// ...
```

若有需要，你可以傳入閉包給 `state` 函式，以攔截傳入元件的屬性並進行修改：

```php
use function Livewire\Volt\{state};
 
state(['count' => fn ($users) => count($users)]);
```

`mount` 函式可用於定義 Livewire 元件的「mount」生命週期掛鉤。傳入元件的參數會注入此方法中。mount 掛鉤所需的其他參數將由 Laravel 的服務容器解析：

```php
use App\Services\UserCounter;
use function Livewire\Volt\{mount};
 
mount(function (UserCounter $counter, $users) {
    $counter->store('userCount', count($users));
 
    // ...
});
```

# 全頁元件
你可以選擇將 Volt 元件渲染為全頁元件，方法是在應用程式的 `routes/web.php` 檔案中定義 Volt 路由：

```php
use Livewire\Volt\Volt;
 
Volt::route('/users', 'user-index');
```

預設情況下，該元件將使用 `components.layouts.app` 版型渲染。你可以透過 `layout` 函式自訂該版型檔案：

```php
use function Livewire\Volt\{layout, state};
 
state('users');
 
layout('components.layouts.admin');
 
// ...
```

你也可以透過 `title` 函式自訂頁面的標題：

```php
use function Livewire\Volt\{layout, state, title};
 
state('users');
 
layout('components.layouts.admin');
 
title('Users');
 
// ...
```

如果標題依賴於元件狀態或外部依賴，你也可以傳入閉包給 `title` 函式：

```php
use function Livewire\Volt\{layout, state, title};
 
state('users');
 
layout('components.layouts.admin');
 
title(fn () => 'Users: ' . $this->users->count());
```

# 屬性
Volt 屬性與 Livewire 屬性一樣，可以方便地在視圖中存取，並在 Livewire 更新間保持其值。你可以使用 `state` 函式來定義屬性：

```php
<?php
 
use function Livewire\Volt\{state};
 
state(['count' => 0]);
 
?>
 
<div>
    {{ $count }}
</div>
```

若狀態屬性的初始值依賴於外部資源（例如資料庫查詢、模型或容器服務），則應將其封裝在閉包中，這樣可以避免過早解析該值：

```php
use App\Models\User;
use function Livewire\Volt\{state};
 
state(['count' => fn () => User::count()]);
```

如果狀態屬性的初始值是透過 Laravel Folio 的路由模型繫結注入，也應封裝在閉包中：

```php
use App\Models\User;
use function Livewire\Volt\{state};
 
state(['user' => fn () => $user]);
```

當然，也可以不指定初始值直接宣告屬性。在這種情況下，其初始值會是 null，或根據傳入元件的屬性自動設定：

```php
use function Livewire\Volt\{mount, state};
 
state(['count']);
 
mount(function ($users) {
    $this->count = count($users);
 
    //
});
```

# 鎖定屬性
Livewire 提供了一個功能來保護屬性，讓你可以「鎖定」它們，防止在客戶端進行修改。使用 Volt 時，只需要在想要保護的 state 上鏈接 `locked` 方法即可：

```php
state(['id'])->locked();
```

# 反應性屬性
在處理巢狀元件時，你可能需要將一個屬性從父元件傳遞到子元件，並在父元件更新該屬性時自動更新子元件。

使用 Volt，可以在希望成為反應性狀態的 state 上鏈接 `reactive` 方法：

```php
state(['todos'])->reactive();
```

# 可模型化屬性
在某些情況下，你可能不想使用反應性屬性。Livewire 提供了模型化功能，允許你透過 `wire:model` 直接在子元件中共享父元件的狀態。

使用 Volt 時，只需在該 state 上鏈接 `modelable` 方法即可：

```php
state(['form'])->modelable();
```

# 計算屬性
Livewire 也允許你定義計算屬性，這對於延遲獲取元件所需資訊非常有用。計算屬性的結果會在單一 Livewire 請求週期中進行「備忘錄」或快取。

要定義計算屬性，可以使用 `computed` 函式。變數名稱會決定計算屬性的名稱：

```php
<?php
 
use App\Models\User;
use function Livewire\Volt\{computed};
 
$count = computed(function () {
    return User::count();
});
 
?>
 
<div>
    {{ $this->count }}
</div>
```

你可以透過鏈接 `persist` 方法將計算屬性的值保存在應用程式的快取中：

```php
$count = computed(function () {
    return User::count();
})->persist();
```

預設情況下，Livewire 將計算屬性的值快取 3600 秒。你可以傳入自訂秒數來修改此數值：

```php
$count = computed(function () {
    return User::count();
})->persist(seconds: 10);
```

# 動作
Livewire 動作提供了一個便捷的方式來監聽頁面互動，並在元件上調用相應方法，進而重新渲染元件。通常，動作是在使用者點擊按鈕時觸發。

要使用 Volt 定義 Livewire 動作，只需要定義一個閉包。該閉包變數的名稱即決定了動作的名稱：

```php
<?php
 
use function Livewire\Volt\{state};
 
state(['count' => 0]);
 
$increment = fn () => $this->count++;
 
?>
 
<div>
    <h1>{{ $count }}</h1>
    <button wire:click="increment">+</button>
</div>
```

在閉包內，`$this` 變數已綁定到底層的 Livewire 元件，讓你可以像在傳統 Livewire 元件中一樣，調用元件內的其他方法：

```php
use function Livewire\Volt\{state};
 
state(['count' => 0]);
 
$increment = function () {
    $this->dispatch('count-updated');
 
    //
};
```

你的動作也可以接收來自 Laravel 服務容器的參數或依賴：

```php
use App\Repositories\PostRepository;
use function Livewire\Volt\{state};
 
state(['postId']);
 
$delete = function (PostRepository $posts) {
    $posts->delete($this->postId);
 
    // ...
};
```

# 無渲染動作
在某些情況下，你的元件可能會宣告一個不會改變 Blade 模板渲染結果的動作。如果是這種情況，可以將動作封裝在 `action` 函式內，並鏈接 `renderless` 方法來略過 Livewire 生命週期中的渲染階段：

```php
use function Livewire\Volt\{action};
 
$incrementViewCount = action(fn () => $this->viewCount++)->renderless();
```

# 保護型輔助方法
預設下，所有 Volt 動作都是「公開」的，可由客戶端呼叫。如果你希望建立一個僅能在動作內部存取的函式，可以使用 `protect` 函式：

```php
use App\Repositories\PostRepository;
use function Livewire\Volt\{protect, state};
 
state(['postId']);
 
$delete = function (PostRepository $posts) {
    $this->ensurePostCanBeDeleted();
 
    $posts->delete($this->postId);
 
    // ...
};
 
$ensurePostCanBeDeleted = protect(function () {
    // ...
});
```

# 表單
Livewire 的表單提供了一個方便的方法來處理表單驗證與提交，且集中於單一類別中。要在 Volt 元件中使用 Livewire 表單，你可以使用 `form` 函式：

```php
<?php
 
use App\Livewire\Forms\PostForm;
use function Livewire\Volt\{form};
 
form(PostForm::class);
 
$save = function () {
    $this->form->store();
 
    // ...
};
 
?>
 
<form wire:submit="save">
    <input type="text" wire:model="form.title">
    @error('form.title') <span class="error">{{ $message }}</span> @enderror
 
    <button type="submit">Save</button>
</form>
```

如你所見，`form` 函式接受一個 Livewire 表單類別名稱。一旦定義，該表單可以在元件中透過 `$this->form` 屬性來存取。

如果你希望為表單使用不同的屬性名稱，可以將名稱作為第二個參數傳入 `form` 函式：

```php
form(PostForm::class, 'postForm');
 
$save = function () {
    $this->postForm->store();
 
    // ...
};
```

# 監聽器
Livewire 的全域事件系統使元件之間能夠互相溝通。如果頁面上存在兩個 Livewire 元件，它們可以利用事件和監聽器來溝通。使用 Volt 時，監聽器可以透過 `on` 函式來定義：

```php
use function Livewire\Volt\{on};
 
on(['eventName' => function () {
    //
}]);
```

如果你需要動態地指定事件監聽器的名稱（例如根據認證使用者或傳入元件的資料），可以傳入閉包給 `on` 函式。該閉包可以接收任何元件參數，以及會由 Laravel 服務容器解析的其他依賴：

```php
on(fn ($post) => [
    'event-' . $post->id => function () {
        //
    },
]);
```

為了方便，也可以使用「點」記法在定義監聽器時參照元件資料：

```php
on(['event-{post.id}' => function () {
    //
}]);
```

# 生命週期掛鉤
Livewire 擁有各種生命週期掛鉤，可用於在元件生命週期中的不同階段執行程式碼。利用 Volt 方便的 API，你可以使用對應的函式來定義這些掛鉤：

```php
use function Livewire\Volt\{boot, booted, ...};
 
boot(fn () => /* ... */);
booted(fn () => /* ... */);
mount(fn () => /* ... */);
hydrate(fn () => /* ... */);
hydrate(['count' => fn () => /* ... */]);
dehydrate(fn () => /* ... */);
dehydrate(['count' => fn () => /* ... */]);
updating(['count' => fn () => /* ... */]);
updated(['count' => fn () => /* ... */]);
```

# 延遲載入佔位符
在渲染 Livewire 元件時，你可以傳遞 `lazy` 參數以延遲元件載入，直到初始頁面完全載入。預設下，Livewire 會在 DOM 中插入 `<div></div>` 標籤作為元件將被載入的位置。

如果你想自訂元件在初始頁面載入時顯示的 HTML 佔位符，可以使用 `placeholder` 函式：

```php
use function Livewire\Volt\{placeholder};
 
placeholder('<div>Loading...</div>');
```

# 驗證
Livewire 提供簡易存取 Laravel 強大驗證功能的方式。利用 Volt 的 API，你可以使用 `rules` 函式定義元件的驗證規則。與傳統的 Livewire 元件一樣，當你調用 `validate` 方法時，這些規則會應用到元件資料上：

```php
<?php
 
use function Livewire\Volt\{rules};
 
rules(['name' => 'required|min:6', 'email' => 'required|email']);
 
$submit = function () {
    $this->validate();
 
    // ...
};
 
?>
 
<form wire:submit.prevent="submit">
    //
</form>
```

如果需要根據認證使用者或資料庫中的資訊動態定義規則，可以傳入閉包給 `rules` 函式：

```php
rules(fn () => [
    'name' => ['required', 'min:6'],
    'email' => ['required', 'email', 'not_in:' . Auth::user()->email]
]);
```

# 錯誤訊息與屬性
要修改驗證時使用的錯誤訊息或屬性名稱，可以在規則定義上鏈接 `messages` 與 `attributes` 方法：

```php
use function Livewire\Volt\{rules};
 
rules(['name' => 'required|min:6', 'email' => 'required|email'])
    ->messages([
        'email.required' => 'The :attribute may not be empty.',
        'email.email' => 'The :attribute format is invalid.',
    ])->attributes([
        'email' => 'email address',
    ]);
```

# 檔案上傳
使用 Volt 時，檔案的上傳與儲存因 Livewire 的支援而變得更為簡單。要在你的函式型 Volt 元件中包含 `Livewire\WithFileUploads` 特徵，可以使用 `usesFileUploads` 函式：

```php
use function Livewire\Volt\{state, usesFileUploads};
 
usesFileUploads();
 
state(['photo']);
 
$save = function () {
    $this->validate([
        'photo' => 'image|max:1024',
    ]);
 
    $this->photo->store('photos');
};
```

# URL 查詢參數
有時候當元件狀態變更時，更新瀏覽器的 URL 查詢參數會很有用。在這種情況下，你可以使用 `url` 方法來指示 Livewire 將 URL 查詢參數與元件狀態同步：

```php
<?php
 
use App\Models\Post;
use function Livewire\Volt\{computed, state};
 
state(['search'])->url();
 
$posts = computed(function () {
    return Post::where('title', 'like', '%'.$this->search.'%')->get();
});
 
?>
 
<div>
    <input wire:model.live="search" type="search" placeholder="Search posts by title...">
 
    <h1>Search Results:</h1>
 
    <ul>
        @foreach($this->posts as $post)
            <li wire:key="{{ $post->id }}">{{ $post->title }}</li>
        @endforeach
    </ul>
</div>
```

Livewire 還支援其他 URL 查詢參數的選項，例如 URL 查詢參數別名，你可以將這些選項傳給 `url` 方法：

```php
use App\Models\Post;
use function Livewire\Volt\{state};
 
state(['page' => 1])->url(as: 'p', history: true, keep: true);
 
// ...
```

# 分頁
Livewire 與 Volt 均完整支援分頁。要在函式型 Volt 元件中包含 Livewire 的 `Livewire\WithPagination` 特徵，可以使用 `usesPagination` 函式：

```php
<?php
 
use function Livewire\Volt\{with, usesPagination};
 
usesPagination();
 
with(fn () => ['posts' => Post::paginate(10)]);
 
?>
 
<div>
    @foreach ($posts as $post)
        //
    @endforeach
 
    {{ $posts->links() }}
</div>
```

與 Laravel 一樣，Livewire 預設的分頁視圖使用 Tailwind CSS 來進行樣式設計。如果你的應用程式使用 Bootstrap，可以在調用 `usesPagination` 時指定你所需的主題來啟用 Bootstrap 分頁主題：

```php
usesPagination(theme: 'bootstrap');
```

# 自訂特徵與介面
若要在函式型 Volt 元件中包含任何自訂特徵或介面，可以使用 `uses` 函式：

```php
use function Livewire\Volt\{uses};
 
use App\Contracts\Sorting;
use App\Concerns\WithSorting;
 
uses([Sorting::class, WithSorting::class]);
```

# 匿名元件
有時候，你可能只希望將頁面中的一小部分轉換為 Volt 元件，而不必將其抽取到獨立檔案中。例如，想像有一個 Laravel 路由回傳以下視圖：

```php
Route::get('/counter', fn () => view('pages/counter.blade.php'));
```

該視圖內容是一個典型的 Blade 模板，包括版型定義與插槽。不過，若你將視圖中的一部分包裹在 `@volt` Blade 指令中，就可以將這部分視圖轉換成一個功能完整的 Volt 元件：

```php
<?php
 
use function Livewire\Volt\{state};
 
state(['count' => 0]);
 
$increment = fn () => $this->count++;
 
?>
 
<x-app-layout>
    <x-slot name="header">
        Counter
    </x-slot>
 
    @volt('counter')
        <div>
            <h1>{{ $count }}</h1>
            <button wire:click="increment">+</button>
        </div>
    @endvolt
</x-app-layout>
```

# 傳遞資料給匿名元件
當渲染包含匿名元件的視圖時，傳遞給視圖的所有資料也會提供給匿名 Volt 元件：

```php
use App\Models\User;
 
Route::get('/counter', fn () => view('users.counter', [
    'count' => User::count(),
]));
```

當然，你也可以將這些資料聲明為 Volt 元件的「state」。在從視圖代理初始化 state 時，只需宣告 state 變數的名稱即可。Volt 會自動使用代理的視圖資料來初始化該 state 的預設值：

```php
<?php
 
use function Livewire\Volt\{state};
 
state('count');
 
$increment = function () {
    // 將新的 count 值存入資料庫...
 
    $this->count++;
};
 
?>
 
<x-app-layout>
    <x-slot name="header">
        Initial value: {{ $count }}
    </x-slot>
 
    @volt('counter')
        <div>
            <h1>{{ $count }}</h1>
            <button wire:click="increment">+</button>
        </div>
    @endvolt
</x-app-layout>
```
