【Laravel實戰】15分鐘搞懂Livewire組件屬性

大家好,我是哥布林工程師,今天繼續用15分鐘的時間來分享 Livewire 組件屬性相關的知識點
公開屬性
Livewire 組件是透過組件類別裡的公開屬性來存取資料,像是下面例子的 message
class HelloWorld extends Component
{
public $message = 'Hello World!';
…
}
Livewire 類別的所有公開屬性將能自動的在視圖內去取用,而不需要你自己去將它們手動傳入到視圖中,一般會需要你自己傳入的情況是針對非公開的屬性才有必要
//Livewire類別
class HelloWorld extends Component
{
public $message = 'Hello World!';
}
//視圖檔案中
<div>
<h1>{{ $message }}</h1>
<!-- Will output "Hello World!" -->
</div>
那關於公開屬性有一些細節你需要去注意:
- 屬性名稱請避開Livewire的保留字,比如:rules以及messages
- 存在 public 屬性的資料將會對前端JS開放,因此請勿儲存敏感的資料
- 屬性只能夠儲存JS所支持的資料型態,比如(string.int.array.boolean),又或者是以下的PHP型別(Stringable, Collection, DateTime, Model, EloquentCollection)
屬性如果沒有設為公開,比如 protected 或者是 private ,將不會在 Livewire更新的時候保存,所以請避免在這些屬性裡頭去儲存狀態
非公開屬性的使用情境
這邊插播一下何時應該使用 protected 甚至是 private 的屬性,簡單說就是考量到安全性不適合或者說不需要對前端程式公開的屬性。針對這兩種修飾子如需要傳送到的前端的視圖,可以透過老方法,也就是view()的第二參數,下面給你一個例子
<?php
namespace App\Http\Livewire;
use App\Models\Article;
class ArticlesGrid extends Component
{
private $articles;
public function render()
{
$this->articles = Article::get();
return view('livewire.articles-grid', compact('articles'));
}
}
屬性初始化
你能夠透過組件的 mount() 來進行屬性的初始化,如下面這個例子
class HelloWorld extends Component
{
public $message;
public function mount()
{
$this->message = 'Hello World!';
}
}
假如你覺得逐一的進行屬性賦值太醜,你也可以改用 $this->fill() 來做
public function mount()
{
$this->fill(['message' => 'Hello World!']);
}
除此之外,Livewire也提供了 $this->reset() 來幫助你重設公開屬性的值,這在你執行完某些操作需要重置屬性值的時候很有用。重置後公開屬性的值會變成初始化的值
public $search = '';
public $isActive = true;
public function resetFilters()
{
$this->reset('search');
重置 search 屬性
$this->reset(['search', 'isActive']);
重置 search 和 isActive 屬性
}
資料綁定
假如你用過前端框架像是 Vue 或是 Angular,你應該就已經熟悉MVVM的觀念了。然而如果你不是太理解的話,資料綁定就是 Livewire 能夠綁定組件的公開屬性到某些 HTML 元素的屬性上,當變更屬性時,這些元素的屬性也會跟著異動
組件類別
class HelloWorld extends Component
{
public $message;
}
組件視圖
<div>
<input wire:model="message" type="text">
<h1>{{ $message }}</h1>
</div>
像剛才這個例子,當使用者在輸入項輸入內容,就會很神奇的發現下面的H1標籤內容跟著異動,就像是魔法一樣,這就是資料綁定的效果
它的原理是,Livewire將會監聽元素的輸入事件,當觸發時,就會發出一個 AJAX請求,取得新資料之後再重新渲染該組件視圖
你能夠加入 wire:model 到任何會派發 input 事件的元素。哪怕是自定義元素甚至是第三方 JS 函式庫都可以,一般能使用 wire:model 的元素包含以下這些:
<input type="text">
<input type="radio">
<input type="checkbox">
<select>
<textarea>
綁定巢狀資料
Livewire 支持綁定陣列裡頭的巢狀資料,透過點語法的方式
<input type="text" wire:model="parent.message">
直接綁定到模型屬性
假如一個 Eloquent 模型被作為公開屬性儲存在 Livewire 組件,你能夠直接綁定它,這是一個例子:
use App\Models\Post;
class PostForm extends Component
{
public Post $post;
protected $rules = [
'post.title' => 'required|string|min:6',
'post.content' => 'required|string|max:500',
];
public function save()
{
$this->validate();
$this->post->save();
}
}
<form wire:submit.prevent="save">
<input type="text" wire:model="post.title">
<textarea wire:model="post.content"></textarea>
<button type="submit">Save</button>
</form>
注意到視圖有直接綁定到模型的 "title" 以及 "content" 屬性,Livewire將會為你搞定模型的解構與重構等工作
注意
有注意到一個名為 $rules 的屬性嗎,你必須定義每一個模型屬性的驗證規則,否則將會跳出錯誤。你甚至可以在Eloquent集合裡去綁定模型。另外此功能限定PHP 7.4 版本以上的專案
use App\Models\Post;
class PostForm extends Component
{
public $posts;
protected $rules = [
'posts.*.title' => 'required|string|min:6',
'posts.*.content' => 'required|string|max:500',
];
public function mount()
{
$this->posts = auth()->user()->posts;
}
public function save()
{
$this->validate();
foreach ($this->posts as $post) {
$post->save();
}
}
}
<form wire:submit.prevent="save">
@foreach ($posts as $index => $post)
<div wire:key="post-field-{{ $post->id }}">
<input type="text" wire:model="posts.{{ $index }}.title">
<textarea wire:model="posts.{{ $index }}.content"></textarea>
</div>
@endforeach
<button type="submit">Save</button>
</form>
取消延遲輸入 Debouncing Input
預設 Livewire 會採用一個 150ms 的文字輸入延遲以避免當使用者在輸入文字時產生過多的網頁請求,以降低伺服器的負擔
假如你想要覆寫此預設行為,Livewire提供你一個名為 "debounce" 的修飾字,假如你想要將輸入項的延遲時間改為0.5秒,你可以加入這個修飾字像這樣:
<input type="text" wire:model.debounce.500ms="name">
Lazy Updating
Lazy Updating 我個人稱之為懶惰更新
預設 Livewire會在每一個輸入事件觸發之後發出請求,這對於像下拉選單這種輸入項不至於發出過快或過多的請求就沒啥問題。然而對於文字輸入項的話就沒有必要這麼頻繁了
在這種情況下,使用 lazy 修飾字來限定監聽 change 事件
<input type="text" wire:model.lazy="message">
現在的 $message 屬性將只會在使用者點到其他的輸入項導致觸發 change事件時才會被更新,從而降低伺服器的負擔,這效果比剛介紹的 Debouncing 更好一些
Deferred Updating
假如你不需要資料被同步的更新,只需要跟著下一次的網頁請求再做更新的話,Livewire提供了 .defer 修飾字來滿足你的需求,同樣是用來降低伺服器的負擔,也是三種手段中效果最好的一個。下面是一個例子:
<input type="text" wire:model.defer="query">
<button wire:click="search">Search</button>
當使用者輸入內容到文字輸入項將不會有任何網路請求被發出,儘管使用者點到其他輸入項來觸發 change 事件也一樣
但當使用者按下 Search 按鈕,Livewire將發出一個網路請求,同時包含新的查詢狀態.以及 Search 行為
透過點擊來觸發更新將大幅度的縮減不必要的網路使用量
計算屬性
Livewire 提供一個用來取用動態屬性的API,這特別適合用於取用來自於資料庫或者是快取的內容
class ShowPost extends Component
{
// Computed Property
public function getPostProperty()
{
return Post::find($this->postId);
}
現在,你可以從組件類別或視圖中去取用 $this->post
class ShowPost extends Component
{
public $postId;
public function getPostProperty()
{
return Post::find($this->postId);
}
public function deletePost()
{
$this->post->delete();
}
}
<div>
<h1>{{ $this->post->title }}</h1>
...
<button wire:click="deletePost">Delete Post</button>
</div>
計算屬性會作為單獨的 Livewire 請求生命週期來進行快取,也就是說,假如你在Blade視圖呼叫 $this->post 五次之後,它將不會再對資料庫進行查詢而是從快取中取出