Laravel 文档阅读:Blade 模版
简介
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 。
图片来源:周星驰电影作品《功夫》。
本作品采用《CC 协议》,转载必须注明作者和本文链接
不错,学习了