介紹 (Introduction)
Blade 是 Laravel 內建的簡單但強大的樣板引擎。與某些 PHP 樣板引擎不同,Blade 並不會限制您在樣板中使用原生 PHP。事實上,所有 Blade 樣板都會被編譯成純 PHP 並快取直到被修改,這表示 Blade 幾乎不會為您的應用程式增加額外負擔。Blade 樣板檔案使用 .blade.php 副檔名,通常儲存在 resources/views 目錄中。
您可以透過全域的 view 輔助函式從路由或 controller 回傳 Blade 檢視。如同在 views 文件所提到,您可以使用 view 輔助函式的第二個參數將資料傳遞給 Blade 檢視:
Route::get('/', function () {
return view('greeting', ['name' => 'Finn']);
});
使用 Livewire 強化 Blade (Supercharging Blade With Livewire)
想要將 Blade 樣板提升到更進階的互動介面,且減少前端框架的複雜性嗎?請查看 Laravel Livewire。Livewire 讓您能撰寫帶有動態功能的 Blade 元件,這些功能通常只有使用像是 React 或 Vue 的前端框架才容易實現。透過 Livewire,您可以在不需大量客戶端渲染或額外建置流程的情況下,建立現代且具反應性的前端介面。
顯示資料 (Displaying Data)
您可以在 Blade 檢視中透過將變數包在大括號來顯示傳入的資料。例如,給定下列路由:
Route::get('/', function () {
return view('welcome', ['name' => 'Samantha']);
});
您可以如此顯示 name 變數的內容:
Hello, {{ $name }}.
[!NOTE] Blade 的
{{ }}echo 陳述式會自動透過 PHP 的htmlspecialchars函式進行轉義,以防範 XSS 攻擊。
您不只限於顯示傳入檢視的變數內容,亦可 echo 任意 PHP 函式的回傳值。事實上,您可以在 Blade echo 陳述式內放入任何您需要執行的 PHP 程式碼:
The current UNIX timestamp is {{ time() }}.
HTML 實體編碼 (Html Entity Encoding)
預設情況下,Blade(以及 Laravel 的 e 函式)會對 HTML 實體進行雙重編碼。如果您想要停用雙重編碼,可在 AppServiceProvider 的 boot 方法中呼叫 Blade::withoutDoubleEncoding:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Blade::withoutDoubleEncoding();
}
}
顯示未轉義的資料 (Displaying Unescaped Data)
預設情況下,Blade 的 {{ }} 陳述式會自動透過 PHP 的 htmlspecialchars 進行轉義。如果您不希望資料被轉義,可以使用下列語法:
Hello, {!! $name !!}.
[!WARNING] 在 echo 使用使用者提供的內容時務必小心。通常應使用經過轉義的
{{ }}語法來避免顯示使用者輸入時發生 XSS。
Blade 與 JavaScript 框架 (Blade And Javascript Frameworks)
由於許多 JavaScript 框架也使用大括號來表示要在瀏覽器中呈現的表達式,您可以在表達式前加上 @ 符號以告知 Blade 保持該表達式不被處理。例如:
<h1>Laravel</h1>
Hello, @{{ name }}.
在此範例中,@ 符號會被 Blade 移除;但 {{ name }} 表達式會保留,讓您的 JavaScript 框架在前端進行渲染。
@ 符號也可以用來跳脫 Blade 指令:
{{-- Blade template --}}
@@if()
<!-- HTML output -->
@if()
呈現 JSON (Rendering Json)
有時您可能會將陣列傳遞到檢視,並希望將其以 JSON 呈現以初始化 JavaScript 變數。舉例:
<script>
var app = <?php echo json_encode($array); ?>;
</script>
不過,您可以改用 Illuminate\Support\Js::from 方法指令來替代手動呼叫 json_encode。from 方法接受與 PHP 的 json_encode 相同的參數,但會確保產出的 JSON 已正確轉義,以便在 HTML 引號內使用。from 方法會回傳一段 JSON.parse 的 JavaScript 陳述,用以在前端還原出物件或陣列:
<script>
var app = {{ Illuminate\Support\Js::from($array) }};
</script>
較新的 Laravel 範本骨架包含 Js facade,可在 Blade 檢視中更方便地使用此功能:
<script>
var app = {{ Js::from($array) }};
</script>
[!WARNING] 您應僅使用
Js::from方法來渲染現有變數為 JSON。Blade 的解析基於正規表示式,嘗試將複雜表達式直接傳入指令可能導致意外錯誤。
@verbatim 指令 (The At Verbatim Directive)
如果在範本中大量顯示 JavaScript 變數,您可以使用 @verbatim 指令包住該區段,避免每個 Blade echo 前都加上 @ 符號:
@verbatim
<div class="container">
Hello, {{ name }}.
</div>
@endverbatim
Blade 指令 (Blade Directives)
除了樣板繼承與顯示資料之外,Blade 也提供了便捷的語法糖來對應常見的 PHP 控制結構(例如條件與迴圈)。這些指令使得在模板中撰寫控制流程更清晰且易讀。
If 條件 (If Statements)
您可以使用 @if、@elseif、@else 與 @endif 指令來建立 if 條件,這些指令與對應的 PHP 敘述行為相同:
@if (count($records) === 1)
I have one record!
@elseif (count($records) > 1)
I have multiple records!
@else
I don't have any records!
@endif
為了方便,Blade 也提供 @unless 指令作為 if (! ...) 的語法糖:
@unless (Auth::check())
You are not signed in.
@endunless
另外,@isset 與 @empty 指令可作為 isset 與 empty 的簡短寫法:
@isset($records)
// $records 已定義且不為 null...
@endisset
@empty($records)
// $records 為 "empty"...
@endempty
驗證相關指令 (Authentication Directives)
@auth 與 @guest 指令可快速判斷目前使用者是否已通過認證或為訪客:
@auth
// 使用者已驗證...
@endauth
@guest
// 使用者尚未登入...
@endguest
如有需要,您也可以在指令中指定要檢查的 guard:
@auth('admin')
// admin guard 已認證的使用者...
@endauth
環境判斷指令 (Environment Directives)
使用 @production 可以在生產環境中顯示特定內容:
@production
// 僅在 production 環境顯示...
@endproduction
或使用 @env 檢查特定環境或多個環境:
@env('staging')
// staging 環境專屬內容...
@endenv
@env(['staging', 'production'])
// staging 或 production 環境顯示...
@endenv
區段指令 (Section Directives)
@hasSection 可用來判斷指定的 section 是否有內容:
@hasSection('navigation')
<div class="pull-right">
@yield('navigation')
</div>
<div class="clearfix"></div>
@endif
如果想判斷某個 section 是否缺少內容,可使用 @sectionMissing:
@sectionMissing('navigation')
<div class="pull-right">
@include('default-navigation')
</div>
@endif
Session 指令 (Session Directives)
@session 指令可用來檢查一個 session 值是否存在。若存在,@session 與 @endsession 之間的內容會被評估。在 @session 內可使用 $value 來顯示該 session 值:
@session('status')
<div class="p-4 bg-green-100">
{{ $value }}
</div>
@endsession
Context 指令 (Context Directives)
@context 指令用於判斷是否有指定的 context 值存在。若存在,@context 與 @endcontext 間的內容將會被評估,並可透過 $value 取得該值:
@context('canonical')
<link href="{{ $value }}" rel="canonical">
@endcontext
Switch 條件 (Switch Statements)
您可以使用 @switch、@case、@break、@default 與 @endswitch 指令建立 switch 結構:
@switch($i)
@case(1)
First case...
@break
@case(2)
Second case...
@break
@default
Default case...
@endswitch
迴圈 (Loops)
Blade 也提供了對 PHP 迴圈結構的簡潔指令,行為與原生 PHP 一致:
@for ($i = 0; $i < 10; $i++)
The current value is {{ $i }}
@endfor
@foreach ($users as $user)
<p>This is user {{ $user->id }}</p>
@endforeach
@forelse ($users as $user)
<li>{{ $user->name }}</li>
@empty
<p>No users</p>
@endforelse
@while (true)
<p>I'm looping forever.</p>
@endwhile
[!NOTE] 在
foreach迴圈中,您可以使用 loop 變數 取得如是否為第一或最後一筆等有用資訊。
在迴圈中,您也可以使用 @continue 與 @break 跳過或終止迭代:
@foreach ($users as $user)
@if ($user->type == 1)
@continue
@endif
<li>{{ $user->name }}</li>
@if ($user->number == 5)
@break
@endif
@endforeach
您也可以將條件直接寫在指令內:
@foreach ($users as $user)
@continue($user->type == 1)
<li>{{ $user->name }}</li>
@break($user->number == 5)
@endforeach
Loop 變數 (The Loop Variable)
在 foreach 迴圈內,Blade 會提供一個 $loop 變數,包含許多有用的屬性,例如目前迭代的索引或是否為第一/最後一筆:
@foreach ($users as $user)
@if ($loop->first)
This is the first iteration.
@endif
@if ($loop->last)
This is the last iteration.
@endif
<p>This is user {{ $user->id }}</p>
@endforeach
在巢狀迴圈中,您可以透過 parent 屬性存取父層的 $loop:
@foreach ($users as $user)
@foreach ($user->posts as $post)
@if ($loop->parent->first)
This is the first iteration of the parent loop.
@endif
@endforeach
@endforeach
$loop 提供的屬性包括:
| 屬性 | 說明 |
| ------------------ | ------------------------------ |
| $loop->index | 當前迭代的索引(從 0 開始) |
| $loop->iteration | 當前迭代次數(從 1 開始) |
| $loop->remaining | 迴圈中剩餘的迭代數量 |
| $loop->count | 陣列或集合中總項目數 |
| $loop->first | 是否為第一筆迭代 |
| $loop->last | 是否為最後一筆迭代 |
| $loop->even | 當前迭代是否為偶數 |
| $loop->odd | 當前迭代是否為奇數 |
| $loop->depth | 目前迴圈的巢狀層級 |
| $loop->parent | 在巢狀迴圈中,父層的 loop 變數 |
條件式 class 與 style (Conditional Classes)
@class 指令可根據條件組合 CSS class。該指令接受一個陣列,鍵為 class(或 class 列表),值為布林表達式;若陣列元素使用數字鍵,該 class 將永遠包含:
@php
$isActive = false;
$hasError = true;
@endphp
<span @class([
'p-4',
'font-bold' => $isActive,
'text-gray-500' => ! $isActive,
'bg-red' => $hasError,
])></span>
<span class="p-4 text-gray-500 bg-red"></span>
相似地,@style 指令可條件式加入行內 CSS:
@php
$isActive = true;
@endphp
<span @style([
'background-color: red',
'font-weight: bold' => $isActive,
])></span>
<span style="background-color: red; font-weight: bold;"></span>
其他 HTML 屬性輔助指令 (Additional Attributes)
為了方便,Blade 提供了多個簡短指令來輸出常見的 HTML 屬性:
@checked(condition):當條件為真時輸出checked。@selected(condition):當條件為真時輸出selected。@disabled(condition):當條件為真時輸出disabled。@readonly(condition):當條件為真時輸出readonly。@required(condition):當條件為真時輸出required。
範例:
<input
type="checkbox"
name="active"
value="active"
@checked(old('active', $user->active))
/>
包含子檢視 (Including Subviews)
@include 指令允許在一個檢視中包含另一個 Blade 檢視。所有在父檢視可用的變數,預設也會在被包含的檢視中可用:
<div>
@include('shared.errors')
<form>
<!-- Form Contents -->
</form>
</div>
您也可以傳遞一個陣列作為額外資料給被包含的檢視:
@include('view.name', ['status' => 'complete'])
若您嘗試 @include 的檢視不存在,Laravel 會拋出錯誤。若該檢視可能不存在,請使用 @includeIf:
@includeIf('view.name', ['status' => 'complete'])
若您想在布林為真時包含檢視,使用 @includeWhen;若為假時才包含,使用 @includeUnless:
@includeWhen($boolean, 'view.name', ['status' => 'complete'])
@includeUnless($boolean, 'view.name', ['status' => 'complete'])
要包含陣列中第一個存在的檢視,可使用 @includeFirst:
@includeFirst(['custom.admin', 'admin'], ['status' => 'complete'])
[!WARNING] 請避免在 Blade 檢視中使用
__DIR__與__FILE__常數,因為它們會指向編譯後、快取的檢視位置。
為集合渲染檢視 (Rendering Views For Collections)
您可以使用 @each 指令在一行中結合迴圈與 include:
@each('view.name', $jobs, 'job')
@each 的第一個參數是每項要渲染的檢視;第二個為要迭代的陣列或集合;第三個為在每次迭代中指定當前項目的變數名。可選的第四個參數為陣列為空時要渲染的檢視:
@each('view.name', $jobs, 'job', 'view.empty')
[!WARNING] 透過
@each渲染的檢視不會繼承父檢視的變數;若子檢視需要父檢視的變數,請改用@foreach+@include。
@once 指令 (The Once Directive)
@once 指令可讓某段內容在單次渲染循環中僅被評估一次。這對於在迴圈中渲染元件時,只想將一段 JavaScript 推到頁面 header 的情境特別有用:
@once
@push('scripts')
<script>
// Your custom JavaScript...
</script>
@endpush
@endonce
與 @push / @prepend 常搭配使用的 @pushOnce 與 @prependOnce 指令也很方便:
@pushOnce('scripts')
<script>
// Your custom JavaScript...
</script>
@endPushOnce
若要在來自不同檔案的推入內容重複時只保留一次,可為 @pushOnce 提供獨一無二的識別字串:
@pushOnce('scripts', 'chart.js')
<script src="/chart.js"></script>
@endPushOnce
原始 PHP (Raw Php)
在某些情況下,將 PHP 程式碼嵌入檢視會很有用。您可以使用 @php 指令在樣板中執行一段原生 PHP:
@php
$counter = 1;
@endphp
或者,若只需引入一個 class,可使用 @use 指令:
@use('App\Models\Flight')
可透過第二個參數為引入的 class 設定別名:
@use('App\Models\Flight', 'FlightModel')
若有多個同命名空間的 class,可一次群組引入:
@use('App\Models\{Flight, Airport}')
@use 指令也支援引入 PHP 函式與常數:
@use(function App\Helpers\format_currency)
@use(const App\Constants\MAX_ATTEMPTS)
註解 (Comments)
Blade 也允許在檢視中定義註解。與 HTML 註解不同,Blade 註解不會出現在最終輸出的 HTML 中:
{{-- This comment will not be present in the rendered HTML --}}
元件 (Components)
元件與插槽提供類似 sections 與 layouts 的功能,但有些開發者會覺得元件的心智模型更易於理解。有兩種撰寫元件的方式:class-based 元件與匿名元件。
要建立 class-based 元件,可使用 make:component Artisan 指令。這會在 app/View/Components 目錄建立元件:
php artisan make:component Alert
該指令也會在 resources/views/components 目錄建立對應的檢視樣板。對於您自己的應用程式,元件會自動在 app/View/Components 和 resources/views/components 目錄中被發現,通常不需額外註冊。
您也可以在子目錄中建立元件:
php artisan make:component Forms/Input
手動註冊套件元件 (Manually Registering Package Components)
若您正在開發包含 Blade 元件的套件,需要手動在 service provider 的 boot 方法中註冊元件:
use Illuminate\Support\Facades\Blade;
public function boot(): void
{
Blade::component('package-alert', Alert::class);
}
註冊後,可使用其標籤別名來渲染:
<x-package-alert/>
或使用 componentNamespace 方法依照慣例自動載入元件 class:
Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
這樣就可以用 package-name:: 語法來渲染套件元件:
<x-nightshade::calendar />
<x-nightshade::color-picker />
渲染元件 (Rendering Components)
要顯示元件,可在 Blade 樣板中使用以 x- 開頭、後接元件 class 名稱(kebab-case)的標籤:
<x-alert/>
<x-user-profile/>
若元件位於 app/View/Components 的子目錄,可使用 . 表示目錄層級:
<x-inputs.button/>
若想條件式渲染元件,可在元件 class 中定義 shouldRender 方法;若回傳 false,元件將不會被渲染。
Index 元件 (Index Components)
當元件目錄名稱與檔案名稱相同時,Laravel 會自動將其視為「根」元件。例如 Card/Card.php 可直接使用 <x-card> 來渲染:
<x-card>
<x-card.header>...</x-card.header>
<x-card.body>...</x-card.body>
</x-card>
傳遞資料給元件 (Passing Data To Components)
您可以透過 HTML 屬性將資料傳遞給 Blade 元件。簡單的原始值可使用一般 HTML 屬性字串;PHP 表達式與變數則需使用 : 前綴的屬性:
<x-alert type="error" :message="$message"/>
元件的所有資料屬性都應在 class 建構子中定義。所有 public 屬性會自動在元件檢視中可用:
<?php
namespace App\View\Components;
use Illuminate\View\Component;
use Illuminate\View\View;
class Alert extends Component
{
public function __construct(
public string $type,
public string $message,
) {}
public function render(): View
{
return view('components.alert');
}
}
渲染時,只要 echo 變數名稱即可顯示:
<div class="alert alert-{{ $type }}">
{{ $message }}
</div>
命名慣例 (Casing)
元件建構子參數應使用 camelCase,在 HTML 屬性中則使用 kebab-case:
<x-alert alert-type="danger" />
簡短屬性語法 (Short Attribute Syntax)
當屬性名稱與變數名稱相同時,可使用簡短語法:
{{-- 簡短語法... --}}
<x-profile :$userId :$name />
{{-- 等同於... --}}
<x-profile :user-id="$userId" :name="$name" />
跳脫屬性渲染 (Escaping Attribute Rendering)
由於某些 JavaScript 框架(如 Alpine.js)也使用冒號前綴,您可以使用雙冒號 :: 告知 Blade 該屬性不是 PHP 表達式:
<x-button ::class="{ danger: isDeleting }">
Submit
</x-button>
元件方法 (Component Methods)
除了 public 屬性外,元件 class 上的任何 public 方法也可在元件樣板中呼叫:
<option {{ $isSelected($value) ? 'selected' : '' }} value="{{ $value }}">
{{ $label }}
</option>
額外依賴 (Additional Dependencies)
若元件需要 Laravel Service Container 的依賴,可在資料屬性之前列出,容器會自動注入:
public function __construct(
public AlertCreator $creator,
public string $type,
public string $message,
) {}
隱藏屬性與方法 (Hiding Attributes And Methods)
若要防止某些 public 屬性或方法暴露給元件樣板,可將它們加入 $except 陣列:
protected $except = ['type'];
元件屬性 (Component Attributes)
我們已經了解如何傳遞資料屬性給元件;但有時您可能需要指定額外的 HTML 屬性,例如 class,這些屬性並非元件運作所必須。一般而言,您會希望將這些屬性向下傳遞到元件樣板的根元素。
所有不屬於元件建構子的屬性會自動加入元件的「attribute bag」,並透過 $attributes 變數在元件中可用:
<div {{ $attributes }}>
<!-- Component content -->
</div>
預設與合併屬性 (Default Merged Attributes)
有時您可能需要指定屬性的預設值,或將額外值合併到元件的某些屬性中。可使用 attribute bag 的 merge 方法:
<div {{ $attributes->merge(['class' => 'alert alert-'.$type]) }}>
{{ $message }}
</div>
若元件這樣使用:
<x-alert type="error" :message="$message" class="mb-4"/>
最終渲染的 HTML 會是:
<div class="alert alert-error mb-4">
<!-- Contents of the $message variable -->
</div>
條件式合併 class (Conditionally Merge Classes)
可使用 class 方法在條件為 true 時合併 class:
<div {{ $attributes->class(['p-4', 'bg-red' => $hasError]) }}>
{{ $message }}
</div>
若需同時合併其他屬性,可鏈結 merge 方法:
<button {{ $attributes->class(['p-4'])->merge(['type' => 'button']) }}>
{{ $slot }}
</button>
非 class 屬性的合併 (Non Class Attribute Merging)
對於非 class 的屬性,merge 方法提供的值會被視為「預設值」。與 class 不同的是,這些屬性不會合併,而是被覆蓋:
<button {{ $attributes->merge(['type' => 'button']) }}>
{{ $slot }}
</button>
使用自訂 type 時:
<x-button type="submit">
Submit
</x-button>
渲染結果為:
<button type="submit">
Submit
</button>
若希望非 class 屬性也能合併(前置),可使用 prepends 方法:
<div {{ $attributes->merge(['data-controller' => $attributes->prepends('profile-controller')]) }}>
{{ $slot }}
</div>
擷取與過濾屬性 (Filtering Attributes)
可使用 filter 方法過濾屬性:
{{ $attributes->filter(fn (string $value, string $key) => $key == 'foo') }}
或使用 whereStartsWith 取得以特定字串開頭的屬性:
{{ $attributes->whereStartsWith('wire:model') }}
相反地,whereDoesntStartWith 可排除以特定字串開頭的屬性:
{{ $attributes->whereDoesntStartWith('wire:model') }}
使用 first 方法可渲染 attribute bag 中的第一個屬性:
{{ $attributes->whereStartsWith('wire:model')->first() }}
使用 has 方法可檢查屬性是否存在:
@if ($attributes->has('class'))
<div>Class attribute is present</div>
@endif
使用 get 方法可取得特定屬性的值:
{{ $attributes->get('class') }}
保留關鍵字 (Reserved Keywords)
以下關鍵字為 Blade 內部使用,不可作為元件的 public 屬性或方法名稱:
datarenderresolveresolveViewshouldRenderviewwithAttributeswithName
Slots(插槽)
您常常需要透過「slots」將額外內容傳遞給元件。元件插槽透過 echo $slot 變數來渲染:
<!-- /resources/views/components/alert.blade.php -->
<div class="alert alert-danger">
{{ $slot }}
</div>
透過將內容注入元件來傳遞 slot:
<x-alert>
<strong>Whoops!</strong> Something went wrong!
</x-alert>
有時元件可能需要在不同位置渲染多個插槽。讓我們修改 alert 元件以支援「title」插槽:
<!-- /resources/views/components/alert.blade.php -->
<span class="alert-title">{{ $title }}</span>
<div class="alert alert-danger">
{{ $slot }}
</div>
使用 x-slot 標籤定義具名插槽的內容。不在 x-slot 內的內容會傳遞給 $slot 變數:
<x-alert>
<x-slot:title>
Server Error
</x-slot>
<strong>Whoops!</strong> Something went wrong!
</x-alert>
可使用 isEmpty 方法判斷插槽是否有內容:
@if ($slot->isEmpty())
This is default content if the slot is empty.
@else
{{ $slot }}
@endif
Scoped Slots
若您曾使用過 Vue 等 JavaScript 框架,可能熟悉「scoped slots」概念。在 Laravel 中,可透過在元件上定義 public 方法或屬性,並在插槽中透過 $component 變數存取:
<x-alert>
<x-slot:title>
{{ $component->formatAlert('Server Error') }}
</x-slot>
<strong>Whoops!</strong> Something went wrong!
</x-alert>
插槽屬性 (Slot Attributes)
與 Blade 元件一樣,您可以為插槽指定額外屬性(如 CSS class):
<x-card class="shadow-sm">
<x-slot:heading class="font-bold">
Heading
</x-slot>
Content
<x-slot:footer class="text-sm">
Footer
</x-slot>
</x-card>
可透過插槽變數的 attributes 屬性來存取這些屬性:
@props([
'heading',
'footer',
])
<div {{ $attributes->class(['border']) }}>
<h1 {{ $heading->attributes->class(['text-lg']) }}>
{{ $heading }}
</h1>
{{ $slot }}
<footer {{ $footer->attributes->class(['text-gray-700']) }}>
{{ $footer }}
</footer>
</div>
行內元件檢視 (Inline Component Views)
對於非常小的元件,同時管理 class 與檢視檔案可能會顯得繁瑣。您可以直接從 render 方法回傳元件的標記:
public function render(): string
{
return <<<'blade'
<div class="alert alert-danger">
{{ $slot }}
</div>
blade;
}
要建立行內檢視元件,可在 make:component 指令使用 --inline 選項:
php artisan make:component Alert --inline
動態元件 (Dynamic Components)
有時您可能需要渲染一個在執行階段才能決定的元件。可使用 Laravel 內建的 dynamic-component:
// $componentName = "secondary-button";
<x-dynamic-component :component="$componentName" class="mt-4" />
手動註冊元件 (Manually Registering Components)
若您正在撰寫套件或將元件放在非慣例目錄,需要手動在 service provider 的 boot 方法中註冊:
use Illuminate\Support\Facades\Blade;
use VendorPackage\View\Components\AlertComponent;
public function boot(): void
{
Blade::component('package-alert', AlertComponent::class);
}
匿名元件 (Anonymous Components)
與行內元件類似,匿名元件也只需單一檔案即可管理,但匿名元件沒有對應的 class。只要在 resources/views/components 目錄中放一個 Blade 檔案即可定義匿名元件:
<x-alert/>
若元件位於 resources/views/components/inputs/button.blade.php,可用 . 表示子目錄:
<x-inputs.button/>
也可以透過 Artisan 建立匿名元件檢視:
php artisan make:component forms.input --view
匿名 Index 元件 (Anonymous Index Components)
當某元件是由多個 Blade 檔組成時,您可能想把這些檔案整理在同一個資料夾下。例如一個 accordion 元件:
/resources/views/components/accordion.blade.php
/resources/views/components/accordion/item.blade.php
這樣可以這樣使用:
<x-accordion>
<x-accordion.item>
...
</x-accordion.item>
</x-accordion>
Blade 允許您將名稱與目錄相同的檔案放在目錄裡,並將它視為該元件的「root」檢視:
/resources/views/components/accordion/accordion.blade.php
/resources/views/components/accordion/item.blade.php
匿名元件的資料屬性與 Attributes (Data Properties Attributes)
由於匿名元件沒有對應的 class,您可以透過 @props 指令指定哪些屬性應視為「資料變數」,其他屬性則會放入 attribute bag:
<!-- /resources/views/components/alert.blade.php -->
@props(['type' => 'info', 'message'])
<div {{ $attributes->merge(['class' => 'alert alert-'.$type]) }}>
{{ $message }}
</div>
存取父元件資料:@aware (Accessing Parent Data)
有時候您會希望在子元件中存取父元件的屬性。可使用 @aware 指令:
<!-- /resources/views/components/menu/index.blade.php -->
@props(['color' => 'gray'])
<ul {{ $attributes->merge(['class' => 'bg-'.$color.'-200']) }}>
{{ $slot }}
</ul>
<!-- /resources/views/components/menu/item.blade.php -->
@aware(['color' => 'gray'])
<li {{ $attributes->merge(['class' => 'text-'.$color.'-800']) }}>
{{ $slot }}
</li>
[!WARNING] >
@aware只能存取以 HTML 屬性明確傳入父元件的資料,無法取得只在@props內設定、但未顯式傳入的預設值。
匿名元件路徑 (Anonymous Component Paths)
除了預設的 resources/views/components 目錄外,您也可以在 service provider 中註冊額外的匿名元件路徑:
public function boot(): void
{
Blade::anonymousComponentPath(__DIR__.'/../components');
}
可選擇性地提供命名空間前綴:
Blade::anonymousComponentPath(__DIR__.'/../components', 'dashboard');
渲染時即可使用:
<x-dashboard::panel />
建立版型 (Layouts)
使用元件的版型 (Layouts Using Components)
多數 Web 應用程式在各頁面之間都共享相同的版面結構。若每個檢視都重複整個 HTML 外框,維護將非常麻煩。幸好,我們可以使用單一 Blade 元件 來定義版型,並在整個應用中重複使用。
定義版型元件
<!-- resources/views/components/layout.blade.php -->
<html>
<head>
<title>{{ $title ?? 'Todo Manager' }}</title>
</head>
<body>
<h1>Todos</h1>
<hr/>
{{ $slot }}
</body>
</html>
套用版型元件
<!-- resources/views/tasks.blade.php -->
<x-layout>
@foreach ($tasks as $task)
<div>{{ $task }}</div>
@endforeach
</x-layout>
如需自訂標題,可使用具名 slot:
<x-layout>
<x-slot:title>
Custom Title
</x-slot>
@foreach ($tasks as $task)
<div>{{ $task }}</div>
@endforeach
</x-layout>
使用樣板繼承的版型 (Layouts Using Template Inheritance)
定義版型 (Defining A Layout)
在 Components 出現之前,Blade 主要透過「樣板繼承」來建立版型:
<!-- resources/views/layouts/app.blade.php -->
<html>
<head>
<title>App Name - @yield('title')</title>
</head>
<body>
@section('sidebar')
This is the master sidebar.
@show
<div class="container">
@yield('content')
</div>
</body>
</html>
繼承版型 (Extending A Layout)
子檢視可透過 @extends 指令指定要繼承的版型,並在其中定義 @section 內容:
<!-- resources/views/child.blade.php -->
@extends('layouts.app')
@section('title', 'Page Title')
@section('sidebar')
@@parent
<p>This is appended to the master sidebar.</p>
@endsection
@section('content')
<p>This is my body content.</p>
@endsection
@yield 也可接受第二個參數作為預設內容:
@yield('content', 'Default content')
表單 (Forms)
CSRF 欄位 (Csrf Field)
所有 HTML 表單都應包含隱藏的 CSRF token 欄位,讓 CSRF 保護 middleware 能驗證請求。可使用 @csrf 指令產生此欄位:
<form method="POST" action="/profile">
@csrf
...
</form>
HTTP Method 欄位 (Method Field)
由於 HTML 表單無法直接送出 PUT、PATCH 或 DELETE 請求,您可以透過隱藏欄位 _method 來「欺騙」HTTP 方法。@method 指令可以自動產生該欄位:
<form action="/foo/bar" method="POST">
@method('PUT')
...
</form>
驗證錯誤 (Validation Errors)
@error 指令可快速檢查特定欄位是否有驗證錯誤訊息。在 @error 區塊中,可使用 $message 來顯示訊息:
<label for="title">Post Title</label>
<input
id="title"
type="text"
class="@error('title') is-invalid @enderror"
/>
@error('title')
<div class="alert alert-danger">{{ $message }}</div>
@enderror
Stacks
Blade 允許您將內容推送到具名的「stack」,並在其他檢視或版型中一次渲染出來。這在指定子檢視所需的 JavaScript 或 CSS 資源時特別好用:
@push('scripts')
<script src="/example.js"></script>
@endpush
若只想在條件為真時推入內容,可使用 @pushIf:
@pushIf($shouldPush, 'scripts')
<script src="/example.js"></script>
@endPushIf
您可以多次對同一個 stack 使用 @push。要在版型中渲染完整內容,可使用 @stack:
<head>
<!-- Head Contents -->
@stack('scripts')
</head>
若想將內容加在 stack 開頭,可使用 @prepend:
@push('scripts')
This will be second...
@endpush
@prepend('scripts')
This will be first...
@endprepend
可使用 @hasstack 判斷 stack 是否有內容:
@hasstack('list')
<ul>
@stack('list')
</ul>
@endif
Service Injection
@inject 指令可用來從 Laravel Service Container 解析服務,並將其注入為樣板中的變數:
@inject('metrics', 'App\\Services\\MetricsService')
<div>
Monthly Revenue: {{ $metrics->monthlyRevenue() }}.
</div>
渲染行內 Blade 樣板 (Rendering Inline Blade Templates)
有時您可能只想將一小段 Blade 字串轉成 HTML,可使用 Blade::render:
use Illuminate\\Support\\Facades\\Blade;
return Blade::render('Hello, {{ $name }}', ['name' => 'Julian Bashir']);
Laravel 會將這類臨時樣板寫入 storage/framework/views。若想在渲染後自動刪除快取檔,可傳入 deleteCachedView: true:
return Blade::render(
'Hello, {{ $name }}',
['name' => 'Julian Bashir'],
deleteCachedView: true
);
渲染 Blade Fragments (Rendering Blade Fragments)
在搭配 Turbo 或 htmx 等前端工具時,您可能只需要回傳 Blade 模板的一小部分。可使用 @fragment / @endfragment:
@fragment('user-list')
<ul>
@foreach ($users as $user)
<li>{{ $user->name }}</li>
@endforeach
</ul>
@endfragment
在渲染檢視時,可透過 fragment 只回傳特定區塊:
return view('dashboard', ['users' => $users])->fragment('user-list');
fragmentIf 可根據條件決定是否只回傳 fragment,否則回傳整個檢視:
return view('dashboard', ['users' => $users])
->fragmentIf($request->hasHeader('HX-Request'), 'user-list');
fragments / fragmentsIf 則支援一次回傳多個 fragment,並將它們串接在一起:
view('dashboard', ['users' => $users])
->fragments(['user-list', 'comment-list']);
擴充 Blade (Extending Blade)
Blade 允許您使用 directive 方法定義自訂指令。當 Blade 編譯器遇到該指令時,會呼叫您提供的 callback,並將指令的表達式傳入:
use Illuminate\\Support\\Facades\\Blade;
Blade::directive('datetime', function (string $expression) {
return "<?php echo ($expression)->format('m/d/Y H:i'); ?>";
});
[!WARNING] 更新自訂指令邏輯後,必須清除快取檢視。可使用
php artisan view:clear。
自訂 Echo Handler (Custom Echo Handlers)
當您在 Blade 中 echo 某個物件時,會呼叫該物件的 __toString 方法。不過,若您無法控制該方法(例如來自第三方套件的 class),可以使用 Blade::stringable 註冊自訂處理:
use Illuminate\\Support\\Facades\\Blade;
use Money\\Money;
Blade::stringable(function (Money $money) {
return $money->formatTo('en_GB');
});
自訂 If 指令 (Custom If Statements)
若只需要簡單的自訂條件判斷,比起撰寫完整指令更方便的方式是使用 Blade::if:
Blade::if('disk', function (string $value) {
return config('filesystems.default') === $value;
});
``;
在樣板中即可使用:
```blade
@disk('local')
<!-- The application is using the local disk... -->
@elsedisk('s3')
<!-- The application is using the s3 disk... -->
@else
<!-- The application is using some other disk... -->
@enddisk
@unlessdisk('local')
<!-- The application is not using the local disk... -->
@enddisk