簡介
Blade 是 Laravel 提供的模版引擎,它簡單、強大。不像其他的 PHP 模版引擎,Blade 允許在檢視中使用原生 PHP 程式碼。實際上,所有的 Blade 檢視最終都會被編譯為原生 PHP 程式碼,快取在 storage/framework/views
資料夾中。Laravel 使用的是這些編譯後的快取檔案,而不是檢視檔案本身,所以,Blade 對於應用程式來說幾乎是零開銷的。當你修改了檢視檔案,那麼它會重新編譯並快取,以便使用。Blade 檢視檔案以 .blade.php
作為字尾名,一般儲存在 resources/views
資料夾中。
模版繼承
定義佈局檔案
Blade 模版引擎的兩個主要優點是 模版繼承 和 區塊。舉一個簡單的例子,一個專案裡,幾乎所有的頁面都是一樣的佈局,這時候就可以把這個佈局提煉出來,作為“母版頁”,繼承了這個母版頁的頁面都有一樣的佈局效果,稱為母版頁的“子頁”。母版頁還叫佈局檔案,佈局檔案就是一個 Blade 檢視:
<!-- Stored in 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>
佈局檔案裡除了基礎的 HTML 標籤,還用了兩個指令:@section
和 @yield
。@section
指令定義區塊,@yield
指令定義區塊裡的內容。
下面,來定義佈局檔案的子頁。
繼承佈局檔案
子頁中,使用 Blade 的 @extends
指令指定“繼承”的佈局檔案,使用 @section
指令為在佈局檔案中使用 @section
和 @yield
指令定義的地方注入內容。
<!-- Stored in resources/views/child.blade.php -->
@extends('layouts.app')
@section('title', 'Page Title')
@section('sidebar')
<p>This is appended to the master sidebar.</p>
@endsection
@section('content')
<p>This is my body content.</p>
@endsection
可以看到,在佈局檔案中使用 @yield
指令定義的地方,在子頁中仍然使用 @section
注入內容;在佈局檔案中使用 @section
指令定義的一個好處是:在子頁中使用 @section
注入內容時,可以使用 @ parent
指令附加(而非重寫)在佈局檔案中的內容,而在佈局檔案中使用 @yield
指令定義的地方是做不到的。 @ parent
指令會在檢視渲染時替換成佈局檔案裡的內容。
注意,與在佈局檔案裡定義的 sidebar
不同的是,子頁裡使用 @endsection
結束,而非 @show
。因為 @endsection
僅用來定義區塊,而 @show
是用來定義、立馬產出區塊的。
從路由中返回檢視檔案,要用到全域性輔助函式 helper
:
Route::get('blade', function () {
return view('child');
});
元件 & 插槽
元件和插槽提供了類似佈局和區塊的優點。而元件和插槽的心智模型更符合直覺。設想一下,在我們的專案中有一個可重用的“彈框”元件:
<!-- /resources/views/alert.blade.php -->
<div class="alert alert-danger">
{{ $slot }}
</div>
{{ $slot }}
變數表示插入的元件內容。構建此元件,是使用 Blade 的 @component
指令。
@component('alert')
<strong>Whoops!</strong> Something went wrong!
@endcomponent
在這個場景裡,{{ $slot }}
變數的內容就是
<strong>Whoops!</strong> Something went wrong!
有時一個元件需要有多個插槽。這時,只要稍許修改元件程式碼,定義一個“標題”插槽,這個插槽稱命名插槽。命名插槽是通過簡單地“列印”匹配其名稱的變數來顯示內容的:
<!-- /resources/views/alert.blade.php -->
<div class="alert alert-danger">
<div class="alert-title">{{ $title }}</div>
{{ $slot }}
</div>
為命名插槽注入內容,使用 @slot
指令。所有不在 @slot
指令裡的內容都會傳遞給元件裡的 $slot
變數:
@component('alert')
@slot('title')
Forbidden
@endslot
You are not allowed to access this resource!
@endcomponent
為元件傳遞額外資料
有時需要為元件傳遞額外資料。為此,可以為 @component
指令傳遞第二個陣列引數,指定要傳遞的額外資料。所有傳遞過去的額外資料作為變數,在元件模版裡都是可取的:
@component('alert', ['foo' => 'bar'])
...
@endcomponent
顯示資料
向 Blade 檢視傳遞資料,是通過將變數包裹在大括號([]
)裡實現的:
Route::get('greeting', function () {
return view('welcome', ['name' => 'Samantha']);
});
下面,就可以用 name
變數顯示內容了:
Hello, {{ $name }}.
{{ }}
是 Blade 檢視的列印語句,當然,列印語句裡不限制只能列印變數內容,也可以使用 PHP 函式。實際上,列印語句這裡可以使用任何 PHP 程式碼:
The current UNIX timestamp is {{ time() }}.
顯示非轉義資料
預設,所有傳遞給 Blade {{ }}
語句的內容都會使用 htmlspecialchars
函式處理、將內容轉義,避免 XSS 攻擊。如果無需轉義輸出的內容,可以使用下面的語法:
Hello, {!! $name !!}.
不過千萬要小心,應該總是優先選擇使用轉義的 {{ }}
語法以避免 XSS 攻擊。因為,有時你很能難避免使用者有意的、無意的資料輸入。
Blade & JavaScript 框架
由於一些 JavaScript 框架也使用花括號({{ }}
)語法解析內容,為了區分開 Blade 和這些用到的 JavaScript 框架,你可以使用 @
符號來告訴 Blade 模版渲染引擎說,這個地方不要動,保持原樣就可以了:
<h1>Laravel</h1>
Hello, @{{ name }}.
上面例子裡,@
符號會從 Blade 中刪除,而 {{ name }}
會保持原樣,用來給你的 JavaScript 框架渲染使用。
@verbatim
指令
如果使用 JavaScript 框架渲染的模版區域很大,這時就可以用 @verbatim
指令包裹這些模版區域,這樣就避免了在每個 Blade 列印語句前都跟上 @
符號的麻煩:
@verbatim
<div class="container">
Hello, {{ name }}.
</div>
@endverbatim
控制結構
除了模版繼承和顯示資料,Blade 還為常見的 PHP 控制結構提供了快捷方式,比如條件判斷和迴圈。這些快捷方式提供了一種非常乾淨、簡潔的控制結構,並且保持了原生 PHP 的形式。
If 語句
構造 if
語句使用 @if
、@elseif
、@else
和 endif
指令。這些指令和原生 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
指令:
@unless (Auth::check())
You are not signed in.
@endunless
除了討論過的條件判斷指令,Blade 還提供了 @isset
和 @empty
指令,都與在原生 PHP 裡的對應功能相同:
@isset($records)
// $records is defined and is not null...
@endisset
@empty($records)
// $records is "empty"...
@endempty
認證
@auth
和 @guest
指令用來判斷當前使用者是認證使用者還是遊客:
@auth
// The user is authenticated...
@endauth
@guest
// The user is not authenticated...
@endguest
Switch 語句
switch
語句使用 @switch
、@case
、@break
、@default
和 @endswitch
指令構建:
@switch($i)
@case(1)
First case...
@break
@case(2)
Second case...
@break
@default
Default case...
@endswitch
迴圈
Blade 還提供了迴圈方面的指令。再一次,這裡的每一個指令都與在原生 PHP 裡的對應功能相同。
@for ($i = 0; $i < 10; $i++)
The current value is {{ $i }}
@endfor
[@foreach](https://learnku.com/users/5651) ($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
在迴圈時,也可以結束或者跳過當前的迭代:
[@foreach](https://learnku.com/users/5651) ($users as $user)
@if ($user->type == 1)
@continue
@endif
<li>{{ $user->name }}</li>
@if ($user->number == 5)
@break
@endif
@endforeach
也可以在一行完成這些操作:
[@foreach](https://learnku.com/users/5651) ($users as $user)
@continue($user->type == 1)
<li>{{ $user->name }}</li>
@break($user->number == 5)
@endforeach
$loop
變數
迴圈時,迴圈內部有一個可用的變數 $loop
。這個變數提供了跟迴圈有關的有用資訊,比如當前迭代的索引、是否是第一次/最後依次迭代等:
[@foreach](https://learnku.com/users/5651) ($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
如果是在巢狀的迴圈裡,就可以使用 $loop
變數的 parent
屬性父級迴圈裡的 $loop
變數:
[@foreach](https://learnku.com/users/5651) ($users as $user)
[@foreach](https://learnku.com/users/5651) ($user->posts as $post)
@if ($loop->parent->first)
This is 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->depth |
當前迴圈的巢狀水平(從 1 開始)。 |
$loop->parent |
內層巢狀迴圈的父級 $loop 變數。 |
註釋
Blade 的註釋語法是 {{-- --}}
。不像 HTMl 註釋,Blade 模版註釋不會出現在最終渲染的 HTML 程式碼裡:
{{-- This comment will not be present in the rendered HTML --}}
PHP
一些情況下,要在檢視裡嵌入 PHP 程式碼。這時可以用 [@php](https://learnku.com/users/10050)
指令在模版裡執行一塊原生 PHP 程式碼。
[@php](https://learnku.com/users/10050)
//
@endphp
雖然 Blade 提供了此功能,但過於頻繁的使用它,可能說明你在模版裡嵌入太多邏輯程式碼了,這並不妥當。
引入子檢視
Blade 允許在一個檢視裡通過 @include
指令引入一個檢視。使用 @include
指令的檢視稱為父級檢視,引入的檢視稱為子檢視。父級檢視裡的所有變數在子檢視裡都是可以獲得的:
<div>
@include('shared.errors')
<form>
<!-- Form Contents -->
</form>
</div>
雖然子檢視會繼承父級檢視裡的所有變數,但是你也可以為引入的子檢視傳遞額外資料,這些資料是以陣列形式傳遞過去的:
@include('view.name', ['some' => 'data'])
當你用 @include
指令引入的檢視檔案不存在時,Laravel 會丟擲錯誤。如果引入的檢視檔案不確定是否存在,應該使用 @includeIf
指令:
@includeIf('view.name', ['some' => 'data'])
如果需要通過一個布林值判斷是否引入子檢視,需要使用 @includeWhen
指令:
@includeWhen($boolean, 'view.name', ['some' => 'data'])
注意,不要使用 __DIR__
和 __FILE__
常量引入 Blade 檢視,因為程式實際使用的是編譯後的、快取在 storage/framework/views
資料夾中的檔案。
為集合渲染檢視
@each
指令整合了迴圈資料和引入檢視的功能:
@each('view.name', $jobs, 'job')
第一個引數是為陣列或者集合中每個元素渲染資料時指定的檢視,第二個引數是要遍歷的陣列或者集合,第三個引數是當前迭代的元素資料在子檢視裡的變數名。在上面的例子裡,我們遍歷的陣列是 jobs
,在 view.name
檢視裡渲染 job
變數,當前迭代的 key 使用 key
變數獲取。
也可以為 @each
指令傳遞第四個引數,這是在給定陣列元素為空時渲染的檢視。
@each('view.name', $jobs, 'job', 'view.empty')
注意,使用 @each
指令渲染的檢視不從父級模版裡繼承變數。如果子檢視還需要這些變數,你應該使用 [@foreach](https://learnku.com/users/5651)
和 @include
指令組合。
堆疊
Blade 允許你用 @push
指令向命名堆疊裡推入內容,命名堆疊以 @stack
指令定義,可以定義在普通檢視或者佈局檔案裡,推入的內容會在檢視或者佈局檔案裡渲染出來。這在,為子檢視新增額外的 JavaScript 庫的場景下,特別有用。
<!-- 在檢視或者佈局檔案中定義堆疊 -->
<head>
<!-- Head Contents -->
@stack('scripts')
</head>
<!-- 在子檢視中推入堆疊內容 -->
@push('scripts')
<script src="/example.js"></script>
@endpush
你可以盡你所需的多次向堆疊推入資料。
服務注入
@inject
指令可從 Laravel 的服務提供者中獲得服務。傳遞給 @inject
指令的第一個引數是一個變數名,獲得的服務就是存放在這個變數裡的,第二個引數就是你要解析的服務的類名或介面名。
@inject('metrics', 'App\Services\MetricsService')
<div>
Monthly Revenue: {{ $metrics->monthlyRevenue() }}.
</div>
擴充套件 Blade
Blade 允許你用 directive
方法建立自定義指令。當 Blade 解析遇到自定義指令時,會將接受到的表示式(expression)放在自定義指令的回撥閉包裡處理。
在下面的例子裡,我們為建立了一個 @datatime($var)
指令,$var
應該是一個 DateTime
例項:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot()
{
Blade::directive('datetime', function ($expression) {
return "<?php echo ($expression)->format('m/d/Y H:i'); ?>";
});
}
/**
* Register bindings in the container.
*
* @return void
*/
public function register()
{
//
}
}
可以看到,我們在傳遞過來的表示式上使用了 format
方法。在這個例子裡,@datatime($var)
指令最終生成的 PHP 程式碼如下:
<?php echo ($var)->format('m/d/Y H:i'); ?>
注意,在更新自定義指令後,需要刪除所有的檢視快取檔案,可以用 view:cache
Artisan 命令實現。
自定義 If 語句
當自定義指令涉及簡單的條件判斷時,使用 Blade::directive
的方式可能會變得稍複雜些。為此,Blade 引入了 Blade::if
方法,使用閉包來快速自定義條件判斷指令。例如,我們定義一個指令,判斷當前的專案環境,可以選擇在 AppServiceProvider
的 boot
裡做這件事情:
use Illuminate\Support\Facades\Blade;
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot()
{
Blade::if('env', function ($environment) {
return app()->environment($environment);
});
}
定義好後,我們使用它:
@env('local')
// The application is in the local environment...
@else
// The application is not in the local environment...
@endenv
是不是很簡單呢??
翻譯、衍生自:https://learnku.com/docs/laravel/5.5/blade 。
圖片來源:周星馳電影作品《功夫》。