簡介
Blade 是 Laravel 提供的模板引擎,它簡單強(qiáng)大。不像其他的 PHP 模板引擎,Blade 允許在視圖中使用原生 PHP 代碼。實(shí)際上,所有的 Blade 視圖最終都會被編譯成原生 PHP 代碼,緩存在 storage/framework/views 文件夾中。Laravel 使用的是這些編譯后的緩存文件,而不是視圖本身,所以,Blade 對于應(yīng)用程序來說是零開銷的。當(dāng)你修改了視圖文件,那么它會重新編譯并緩存,以便使用。Blade 視圖以 blade.php 為后綴名,一般存放于 resources/views 文件夾中。
模板繼承
定義布局文件
Blade 模板引擎的主要兩個優(yōu)點(diǎn)是 “模板繼承” 和 “區(qū)塊”。舉一個簡單的例子,一個項目里,幾乎所有的頁面都是一樣的布局,這時候就可以把這個布局提煉出來,作為母版頁,繼承了這個母版頁的的頁面都有一樣的布局效果,成為母版頁的子頁。母版頁還叫布局文件,布局文件就是一個 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>
布局文件里除了基礎(chǔ)的 HTNL 標(biāo)簽,還使用了兩個指令:@section 和 @yield 。@section定義區(qū)塊,@yield 定義區(qū)塊里的內(nèi)容。
下面。來定義布局文件的子頁。
繼承布局文件
子頁中,使用 Blade 的 @extends 指令指定 “繼承” 的布局文件,使用 @section 指令為在布局文件中使用 @section 和 @yield 指令的地方注入內(nèi)容:
<!-- Stored in resources/views/child.blade.php -->
@extends('layouts.app')
@section('title','Page Title')
@section('sidebar')
<p> This is appended to the master sideebar</p>
@endsection
@section('content')
<p>This ismy body content. </p>
@endsection
可以看到,在布局文件中使用 @yield 指令的地方,在子頁中仍然使用 @section 注入內(nèi)容;在布局文件中使用 @section 指令定義的一個好處是:在子頁中使用 @section 注入時,可以使用 @parent 指令附加(而非重寫)在布局文件中的內(nèi)容,而在布局文件中使用 @yield 指令定義的地方是做不到的。@parent 指令會在視圖渲染的時替換成布局文件里的內(nèi)容。
注意,與在文件布局里定義的 sidebar 不同的是,子頁里使用 @endsection 結(jié)束,而非 @show 。因?yàn)?@endsection 僅用來定義區(qū)塊,而 @show 是用來定義、立馬產(chǎn)出區(qū)塊的。
從路由中直接返回視圖文件,要用到全局輔助函數(shù) helper :
Route::get('blade', function(){
return view('child');
})
組件&插槽
組件和插槽提供了類似布局和區(qū)塊的優(yōu)點(diǎn)。而組件和插槽的心智模型更符合直覺。設(shè)想一下,在我們的項目中有一個可重復(fù)的 “彈框” 組件:
<!-- Stored resource/views/alter.blade.php -->
<div class="alter alter-danger">
{{ $slot }}
</div>
{{ $slot }} 表示插入組建的內(nèi)容。構(gòu)建此組件,是使用 Blade 的@component指令:
@component('alter')
<strong>Whoops!</strong> Something went wrong!
@endcomponent
在這個場景里,{{ $slot }} 變量的內(nèi)容是:
<strong>Whoops! </strong> Something went wrong!
有時一個組件需要多個插槽。這時,只需要稍改組件代碼,定義一個 “標(biāo)題” 插槽,這個插槽稱命名插槽。命名插槽是通過簡單的 “打印” 匹配其名稱的變量來顯示內(nèi)容的:
<!-- Stored resources/views/alter.blade.php-->
<div class="alter alter-danger">
<div class="alter-title">{{ $title }}</div>
{{ $slot }}
</div>
為名名插槽注入內(nèi)容,使用 @slot 指令。所有不在 @slot 指令里的內(nèi)容都會傳遞給組件里的 $slot 變量。
@compontent ('alter')
@slot('title')
Forbidden
@endslot
You are not allowed to access this resource!
@edcompontent~
為組建傳遞額外數(shù)據(jù):
有時需要為組建件遞額外數(shù)據(jù)。為此,可以為 @conponent 指令傳遞第二個數(shù)組參數(shù)。指定要傳遞的額外。據(jù)所有過去的額外數(shù)據(jù)作為變量,在組件模板里都可以取的:
@component
<compontent('alter', ['foo' => 'bar');
.....
@endCompontent
顯示數(shù)據(jù)
向 Blade 視圖傳遞數(shù)據(jù),是通過將變量包裹在 [ ] 里實(shí)現(xiàn)的:
Route::get('greeting', function(){
return view('welcome', [ 'name' => ''Samantha']);
})
下面就可以使用 name 變量顯示內(nèi)容了:
Hello! {{ $name }}
{{ }} 是 Blade 視圖的打印語句,當(dāng)然,打印語句里不限制只能打印變量內(nèi)容,也可以使用 PHP 函數(shù)。實(shí)際上,打印語句這里可以使用任何 PHP 代碼:
The current UNIX timestamp is {{ time() }}
顯示非轉(zhuǎn)移數(shù)據(jù)
默認(rèn),所有傳遞給 Blade {{ }} 語句的內(nèi)容都會使用 htmlspecialchar 函數(shù)處理、將內(nèi)容轉(zhuǎn)義,避免 XSS 攻擊。如果無需轉(zhuǎn)義輸出的內(nèi)容,可以使用下面語法:
Hello! {{!! $name !!}}.
不過千萬要小心,應(yīng)該優(yōu)先選擇使用轉(zhuǎn)義的 {{ }} 語法避免 XXS 攻擊。因?yàn)?,有時你很難避免用戶有意的、無意的數(shù)據(jù)輸入。
Blade & JavaScript 框架
由于一些 JavaScript 框架也使用花括號 {{ }} 語法解析內(nèi)容,為了區(qū)分開 Blade 和這些用到的 JavaScript 框架,你可以使用 @ 符號來告訴 Blade 模板引擎說,這個地方不要動,保持原樣就可以了:
<h1>Laravel</h1>
Hello! @{{ $name }}
上面例子里, @ 符號會從 Blade 中刪除,而 {{ $name }} 會保持原樣,用來給你的 JavaScript 框架渲染使用。
@verbatim 指令
如果使用 JavaScript 框架渲染的模塊區(qū)域很大,這時就可以使用 @verbatim 指令包裹這些模板區(qū)域,這樣就避免了在每個Blade 打印語句前都跟上 @ 符號的麻煩:
@verbatim
<div class="container">
Hello! {{ $name }}
</div>
@endverbatim
控制結(jié)構(gòu)
除了模板繼承和顯示數(shù)據(jù),Blade 還未常見的 PHP 結(jié)構(gòu)提供了快捷方式,比如條件談判和循環(huán)。這些快捷方式提供了一種非常干凈、簡潔的控制結(jié)構(gòu),并且保持了原生 PHP 的形式。
if 語句
構(gòu)造 if 語句使用 @if 、@elseif 、@else 和 endif 指令。這些指令和原生 PHP 的控制功能一一對應(yīng):
@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.
@endless
除了討論過的條件判斷指令,Blade 還提供了 @isset 和 @empty 指令,都與在原生 PHP 里的對應(yīng)功能相同:
@isset($recodes)
// $recodes is defined and is not null ...
@endisset
@empty($recodes)
// $recodes is "empty" ...
@endempty
認(rèn)證
@auth 和 @guest 指令用來判斷當(dāng)前用戶是認(rèn)證用戶還是游客:
@auth
// The user is authenticated...
@endauth
@guest
// The useris not authenticated...
@endguest
Switch 語句
switch 語句使用 @switch 、@case 、@break、@default 和 @endswitch 指令構(gòu)建:
@switch($i)
@case(1)
First case...
@break;
@case(2)
Second case...
@break;
@default
Default case...
@endswitch
循環(huán)
Blade 還提供了循環(huán)方面的指令。再一次聲明:這里的每一個指令都與原生 PHP 里的對應(yīng)功能相同。
@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 user </p>
@endforelse
@while(true)
<p>I'm looping forever.</p>
@endwhile
在循環(huán)時可以結(jié)束或跳出當(dāng)前的迭代:
@foreach($users as $user)
@if ($user->type == 1)
@continue
@endif
<li>{{ $user->name }}</li>
@break($user->number == 5)
@endforeach
$loop 變量
在循環(huán)時,內(nèi)部有一個可用變量 $loop 。這個變量提供了跟循環(huán)有關(guān)的有用信息,比如當(dāng)前迭代的索引、是否是第一次、最后一次迭代等:
@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
如果是在嵌套的循環(huán)里,就可以使用 $loop 變量的 parent 屬性父級循環(huán)里的 $loop 變量:
@foreach($users as $user)
@foreach($user->posts as $post)
@if ($loop->parent->first)
This is first iteration of the parent loop.
@endif
@endforeach
@endforeach
$loop 變量提供的有用屬性列舉如下:

注釋
Blade 的注釋語法是 {{-- --}} 。不像 HTML 注釋,Blade 模板注釋不會出現(xiàn)在最終渲染的 HTML 代碼里:
{{-- This is comment will not be present in the rendered HTML --}}
PHP
Blade 允許在一個視圖里通過 @include 指令引入一個視圖。使用 @include 指令的視圖稱為父級視圖,引入的視圖稱為子視圖。父級視圖的所有變量在子視圖里都是可以獲得的:
<div>
@include('shared.error')
<form>
<!-- Form Contents -->
</form>
</div>
雖然子視圖會繼承父級視圖內(nèi)的所有變量,但是你也可以為引入的子視圖傳遞額外數(shù)據(jù),這些數(shù)據(jù)是以數(shù)組的形式傳遞過去的:
@include('view.name',['some' => 'data'])
當(dāng)你用 @include 引入的視圖不存在時, Laravel 會拋出錯誤。如果引入的試圖不確定是否存在,應(yīng)該使用 @includeIf 指令:
@includeIf('view.name', ['some' => 'data'])
如果要通過一個布爾值判斷是否引入子視圖,需要使用 @includeWhen 指令:
@includeWhen($boolean, 'view.name', ['some' => 'data'])
注意:不要使用 __DIR__ 和 __FILE__ 常量引入 Blade 視圖,因?yàn)槌绦驅(qū)嶋H使用的是編譯后的、緩存在 storage/framework/views 文件夾中的文件。
為集合渲染視圖
@each 指令整合了循環(huán)數(shù)據(jù)和引入視圖的功能:
@each(''view.name', $job, 'job')
第一個參數(shù)是為數(shù)組或者集合中每個元素渲染數(shù)據(jù)時指定的視圖,第二個參數(shù)是要便利的數(shù)組或集合,第三個參數(shù)是當(dāng)前迭代的元素數(shù)據(jù)在子視圖中的變量名。在上面的例子里,我們遍歷的數(shù)組是 jobs , 在 view.name 視圖里渲染 job 變量,當(dāng)前迭代的 key 使用 key 變量獲取。
也可以為 @each 指令傳遞第四個參數(shù),這是在給定數(shù)組元素為空時渲染的視圖:
@each($bollean, 'view.name', [ 'some' => 'data'], 'view.empty')
注意:使用 @each 渲染的視圖不從父級模板里繼承變量。如果子視圖還需要這些變量,你應(yīng)該使用 foreach 和 @include 指令組合。
堆棧
Blade 允許你使用 @push 指令向命名堆棧里推入內(nèi)容,命令堆棧以 @stack 指令定義,可以定義在普通視圖或者布局文件里,推入的內(nèi)容會在視圖或者布局文件里渲染出來。這在為子視圖添加額外的 JavaScript 庫的場景下特別有用。
<!-- 在視圖或者布局文件里定義堆棧 -->
<head>
<!-- Head Contents -->
@stack('scripts')
</head>
<!-- 在子視圖中推入堆棧內(nèi)容 -->
@push('scripts')
<script src="/example.js"></script>
@endpush
你可以盡你自己所需的多次向堆棧推入數(shù)據(jù)。
服務(wù)注入
@inject 指令可以從 Laravel 的服務(wù)提供者中獲得服務(wù)。傳遞給 @inject 指令的第一個參數(shù)是一個變量名,獲得的服務(wù)就是存放在這個變量里,第二個參數(shù)就是你要解析的服務(wù)的類名或接口名。
@inject('metrics', 'App\Services\MetricesService')
<div>
Monthly Revenue: {{ $metrics->monthlyRevenue() }}.
</div>
擴(kuò)展 Blade
Blade 允許你使用 directive 方法創(chuàng)建自定義指令。當(dāng) Blade 方法遇到自定義指令時,會將接收到的表達(dá)式 (expression)放在自定義指令的回調(diào)閉包里處理。
在下面的例子里,我們創(chuàng)建了一個 @datatime($var) 指令,$var應(yīng)該是一個DateTime 實(shí)例:
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()
{
//
}
}
可以看到,我們在傳過來的表達(dá)式上使用了 format 方法。在這個例子里,@datetime($var) 指令最終生成的 PHP 代碼如下:
<?php echo ($var)->format('m/d/Y H:i'); ?>
注意:在更新自定義指令后,需要刪除所有的視圖文件,可以用 view:cache Artisan 命令實(shí)現(xiàn)。
自定義 if 語句
當(dāng)自定義涉及簡單的條件判斷時,使用 Blade::directive 的方式可能會變得稍復(fù)雜些。為此, Blade 引入了 Blade::if 方法使用閉包來快速自定義條件判斷指令。例如,我們定義一個指令,判斷當(dāng)前的項目環(huán)境,可以選擇在 AppServiceProvider 的 boot 里做這件事情:
use Illuminate\Support\Facades\Blade;
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot()
{
Blade::if('env', function($expression){
return app()->environment($environment);
})
}
定義好后,咱使用它:
@env('local')
// The application is in the local environment...
@else
// The application is not in the local environment...
@endenv
是不是很簡單呢 ??