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>
```