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