商業,創業,美食,葡萄酒,閱讀,網路科技。
這是我的 FB粉專 以及 IG,我比較常使用 Threads,歡迎大家追蹤互動~
https://laravel.com/docs/8.x/blade
******* 以下文章是基於 Laravel 8 *******
Component & Slot 的概念從 Laravel 5.4 開始就有了,一直演進到目前是 Laravel 8。寫法上觀念上都有很大的改變,也看得出來 Laravel 前端 template 是越來越往 component based 的框架在走。前幾年開始到目前 (2021年5月),似乎 component based 的前端框架還是主流,這點可以再做觀察。
本篇文章主要是講 Laravel 8 的 Component & Slot。如果基礎 Laravel Blade Templates 還不熟的讀者,可以參考我之前的文章:Laravel Blade Templates:The Basics
首先,Component 分為兩種:class based component 和 anonymous component。簡單來說,class based component 是由一支 Class 檔案搭配一支 view template 檔案。而 anonymous component 則是簡單只有一支 view template 檔案。
Class based component
如上方 Laravel 8 官方文件,要建立一個 class based component,可以下這個指令
php artisan make:component Alert
這個指令會在
- App\View\Components 新增一支 Class 檔案。
- resources/views/components 新增一支 view template 檔案。
建立 class based component 時也可以建立檔案結構 (比較好管理)
php artisan make:component Forms/Input
這個指令會在
- App\View\Components\Forms 新增一支 Class 檔案。
- resources/views/components/forms 新增一支 view template 檔案。
調用端如何使用?用 x- 開頭接 component class name,以小寫 dash (-) 分隔的方式
<x-alert/>
<x-user-profile/>
如果 component 是有檔案結構,如上例 Forms/Input,除了小寫 dash (-) 分隔的規則,檔案上下層以 dot (.) 分隔
<x-forms.input/>
Component 如何接收調用端的資料
component class 用 constructor 接收調用端的資料 (下例並非完整,僅用於勾勒出 component 如何接收資料,完整 class 請參考官方文件)
class Alert extends Component
{
...
public function __construct($type, $message)
{
$this->type = $type;
$this->message = $message;
}
...
}
這樣調用端就可以將 type, message 傳進 component。
注意傳 php 變數的屬性前要加 colon (:)
<x-alert type="error" :message="$message"/>
再看一下上方 class Alert。component class 的 view 可以直接使用 component class 的 public 變數,不用特別傳到 view 裡 (跟一般 Laravel controller 不同)。以上方 class Alert 來說,$type, $message 應該是 public 變數,class Alert 的 view 可以直接使用。
Component Attributes
所有不在 component constructor 接收的變數,都會自動存到 component 的 “attribute bag”,其實就是 component 預設的 $attributes 變數。所以如果沒有特別設定 component constructor,想直接顯示調用端所下的屬性,component view 可以這樣寫
<div {{ $attributes }}>
<!-- Component content -->
</div>
很多時候,component view 本身可以設定 css class,同時也開放調用端下 css,可以使用 $attributes->merge
<div {{ $attributes->merge(['class' => 'alert alert-'.$type]) }}>
{{ $message }}
</div>
還記得 class Alert,$type 變數是 constructor 接收。如果調用端這樣寫
<x-alert type="error" :message="$message" class="mb-4"/>
上面 $attributes->merge 就會把 class=”alert alert-error” 跟 “mb-4” 合併在一起,最後產出的 html 是
<div class="alert alert-error mb-4">
<!-- Contents of the $message variable -->
</div>
html 屬性不是只有 css class,其他屬性不見得能下多個值。如果 $attributes->merge 這樣寫
<button {{ $attributes->merge(['type' => 'button']) }}>
{{ $slot }}
</button>
當調用端 x-button 沒有指定 type,最後產出的 html button type=”button”。
當調用端 x-button 有指定 type,最後產出的 html button type=調用端指定的值。
所以非 css class 的屬性,$attributes->merge 的行為是「覆寫」,而非「合併」
Slot
除了上段討論的 $attributes 變數,component 也預設了 $slot 變數。如果 component view 是
<span class="alert-title">{{ $title }}</span>
<div class="alert alert-danger">
{{ $slot }}
</div>
調用端 x-alert 中間夾的內容,會自動填到 $slot 變數中。但 component view 還有一個 $title,此時調用端就要新增一個 <x-slot name=”title”>,中間夾的內容會填入 $title (這個地方 title 也是一個 slot)
<x-alert>
<x-slot name="title">
Server Error
</x-slot>
<strong>Whoops!</strong> Something went wrong!
</x-alert>
slot 可以看成是一個比較快,不需要 component class,又可以將資料傳到 component 的機制。
Anonymous Component
anonymous component 不需要 component class,在 resources/views/components 新增一支 view template 檔案,調用端就可使用了 <x-your-component/>
resources/views/components 可以有檔案結構,調用端檔案上下層以 dot (.) 分隔,例如 <x-forms.input/>,這些都跟 class based component 相同。
因為沒有 component class,還是要有一個機制可以傳入指定變數。anonymous component 使用 @props directive,其中並可指定變數的預設值,例如下面 type 的預設值是 “info”
@props(['type' => 'info', 'message'])
<div {{ $attributes->merge(['class' => 'alert alert-'.$type]) }}>
{{ $message }}
</div>
使用 Component 做版面 layout
Laravel 演進到現在 (Laravel 8),其實是鼓勵開發者用 component 做版面 layout,而不是用傳統的 @extends, @yield, @section 等。
用 component 做版面 layout,主要是上面提到的 slot 機制。例如 resources/views/components/layout.blade.php
<html>
<head>
<title>{{ $title ?? 'Todo Manager' }}</title>
</head>
<body>
<h1>Todos</h1>
<hr/>
{{ $slot }}
</body>
</html>
child view:resources/views/tasks.blade.php。其中 <x-layout> 中整段會填到 $slot,也注意到 title slot 的寫法,可以傳入 $title 變數。
<x-layout>
<x-slot name="title">
Custom Title
</x-slot>
@foreach ($tasks as $task)
{{ $task }}
@endforeach
</x-layout>
相較於傳統的 @extends, @yield, @section 佈局方式
- child view 的角色是 component 的調用端
- master view 的角色是 component 本身
如果對 design pattern 有了解的讀者,傳統的佈局方式跟 component based 的佈局方式,是類似的 design pattern,所以使用上邏輯是很像的。
也可以參考我 Design Pattern 系列的文章
結論
本文主要是希望能勾勒出 Laravel 8 component 的使用,與使用 component 做版面 layout 的方式。沒有談太多細節、或變化型的使用,很多細節的部分應該是由開發者自行斟酌使用。Laravel 前端目前看起來是往 component based 的方向在走,習慣於 @extends, @yield, @section 佈局方式的開發者,可以考慮使用 component。
商業,創業,美食,葡萄酒,閱讀,網路科技。