談談 Livewire 4 的 Json Attribute
Livewire v4 在前幾天終於正式發佈啦!這次同樣帶來了翻天覆的的改變 😆,最大的亮點莫過於 SFC(Single-file Component) 了。現在你可以將前後端的邏輯都寫在同一個檔案中,讓開發體驗更接近現代化的前端框架。
<?php
use Livewire\Component;
new class extends Component
{
// 這裡可以寫後端邏輯
};
?>
<div></div>
如果你還是習慣將前後端邏輯分成兩個檔案,Livewire 4 依然支援 MFC(Multi-file Component)的模式。
雖然正式版最近才上線,但我在去年 Livewire 4 還在 Beta 階段時,就已經將自己的部落格升級了,也有協助回報了一些 Bug 🐛。在這次的正式版中,推出了一些 Beta 版本沒有的新功能,其中最讓我眼前一亮的就是新的 #[Json] Attribute。
下面是官方文件示範如何使用 #[Json] Attribute 實作常見的文章搜尋功能:
<?php
declare(strict_types=1);
use App\Models\Post;
use Illuminate\Database\Eloquent\Collection;
use Livewire\Attributes\Json;
use Livewire\Component;
new class extends Component
{
// 將這個方法標註為 json endpoint
// 你可以在前端使用 $wire.search() 呼叫這個方法,並取得回傳值
#[Json]
public function search(string $query): Collection
{
// json endpoint 的回傳值會被轉為前端可以直接使用的 json 格式
return Post::search($query)
->take(10)
->get();
}
};
?>
<div x-data="{
query: '',
posts: [],
onInput() {
// 呼叫後端的 search() 方法取得文章搜尋結果
$wire.search(query)
.then(data => posts = data)
}
}">
<input
type="text"
x-model="query"
x-on:input.debounce="onInput"
placeholder="搜尋文章..."
>
<ul><template x-for="post in posts" :key="post.id">
<li x-text="post.title"></li>
</template>
</ul>
</div>
#[Json] Attribute 可以將 Livewire 組件中的方法變為 JSON 端點(JSON Endpoint)。當你在前端使用 JavaScript 調用此方法時,Livewire 會直接回傳 JSON 格式的資料,而且不會觸發組件的重新渲染(Re-rendering)流程。
如果你的方法中有使用到 Validation 的話,那麼在 Validation 失敗的時候,Livewire 也會回傳 JSON 格式的錯誤訊息。
{
status: 422, // HTTP 狀態碼 (validation 失敗會回傳 422)
body: null, // 原始回應的內容 (validation 失敗會回傳 null)
json: null, // JSON 格式的回應 (validation 失敗會回傳 null)
errors: {...} // Validation 的錯誤訊息
}
你可以使用 catch() 來處理錯誤訊息。
$wire
.save()
.then((data) => {
// 處理成功回應
console.log(data);
})
.catch((e) => {
if (e.status === 422) {
// 處理 validation 失敗
console.log(e.errors);
}
});
你可能會想,這不就是前端與後端最常見的交互方式嗎?透過 JSON 格式的資料。
提升 Livewire 效能的方式,就是不使用 Livewire!?
實際上,隨著 Livewire 的普及,關於它的優缺點與效能優化的討論也越來越多。例如這篇在 Reddit 上關於 Livewire 優缺點的討論中,有一位開發者的建議獲得了廣泛認同。
他提到,如果你的畫面上有一個區塊需要根據狀態頻繁切換顯示,盡量不要這樣寫:
@if ($isEnabled)
<div>content here</div>
@endif;
因為這會觸發 Livewire 的後端請求與 DOM 更新。他建議使用 Alpine.js 的 x-show,效能會好得多:
<div x-cloak x-show="$wire.isEnabled">
content here
</div>
Laravel 的 Josh Cirre 也有針對這個討論串拍攝影片分享看法。
而在該影片的留言區,Filament 的核心開發者 Alex Six 提到,他們為了提升 Filament 4 的效能花了很多心力,而核心的策略就是:盡可能的使用 Alpine.js。

Livewire 的核心開發者 Ryan Chandler,也在去年的 Wire Live 議程中也分享了如何在開發 Livewire App 時充分利用 Alpine.js 的優勢。
這些效能的改善的建議背後都有一個共同點,那就是:
盡量避免 Livewire 重新渲染。
原因很簡單,一旦觸發 Livewire 組件的更新,伺服器回應的 Payload 就會包含一大段重新渲染後的 HTML:
{
"components": [
{
"snapshot": "...",
"effects": {
"returns": [null],
// [!code highlight:1]
"html": "一大片的 HTML ()..."
}
}
],
"assets": []
}
當互動頻率高或 DOM 結構複雜時,相較於只傳遞單純的 JSON 資料,這種傳遞大量 HTML 的方式顯然會造成較大的效能負擔。
#[Json] 的妙用
當我看到這個新的 #[Json] Attribute 時,我腦海中立刻冒出了一極端的想法:
我好像可以完全不觸發 Livewire 的重新渲染,任何需要跟後端取得資料的地方都使用 JSON Endpoint,並使用 Alpine.js 在前端渲染頁面。這樣的使用方式就好像我們熟悉的純 API + 前端框架的模式一般,把效能最大化。
但仔細一想,這樣好像就喪失了 Livewire 的最大魅力,#[Json] Attribute 最大的優點,我是它能讓我們能在 Livewire 組件內定義「輕量級的 API 端點」。這意味著:
- 保留開發體驗:你依然寫著熟悉的 PHP 方法,直接調用 Eloquent 模型,享受 Laravel 的生態系。
- 提升前端效能:當前端需要資料時(例如搜尋建議、動態圖表數據),透過
#[Json]方法獲取純數據,再交由 Alpine.js 進行輕量的 DOM 更新。
這避免了傳統 Livewire「牽一髮動全身」的 HTML 重新渲染,同時也不需要為了這一點點互動特的去寫一支獨立的 API Controller。
總結來說,#[Json] 讓 Livewire 組件能更靈活的的在「伺服器端渲染」與「客戶端互動」之間切換,是開發高效能 Livewire 應用不可或缺的新利器。
官方文件對於使用
#[Json]的建議如下:
- 實作搜尋建議 (Autocomplete)。
- 為前端圖表或第三方套件加載動態數據。
- 任何只需要數據而不需要更新 HTML 結構的互動。