# Blade 模板
- [简介](#introduction)
- [模板继承](#template-inheritance)
- [定义布局](#defining-a-layout)
- [继承布局](#extending-a-layout)
- [显示数据](#displaying-data)
- [Blade & JavaScript 框架](#blade-and-javascript-frameworks)
- [流程控制](#control-structures)
- [If 语句](#if-statements)
- [Switch 语句](#switch-statements)
- [循环](#loops)
- [循环遍历](#the-loop-variable)
- [注释](#comments)
- [PHP](#php)
- [`@once` 指令](#the-once-directive)
- [表单](#forms)
- [CSRF 字段](#csrf-field)
- [Method 字段](#method-field)
- [验证错误](#validation-errors)
- [组件](#components)
- [组件显示](#displaying-components)
- [组件传参](#passing-data-to-components)
- [属性管理](#managing-attributes)
- [插槽](#slots)
- [內联组件视图](#inline-component-views)
- [匿名组件](#anonymous-components)
- [动态组件](#dynamic-components)
- [引入子视图](#including-subviews)
- [渲染视图集合](#rendering-views-for-collections)
- [堆栈](#stacks)
- [服务注入](#service-injection)
- [Blade 扩展](#extending-blade)
- [自定义 If 语句](#custom-if-statements)
## 简介
Blade 是 Laravel 提供的一个简单而又强大的模板引擎。和其他流行的 PHP 模板引擎不同,Blade 并不限制你在视图中使用原生 PHP 代码。所有 Blade 视图文件都将被编译成原生的 PHP 代码并缓存起来,除非它被修改,否则不会重新编译,这就意味着 Blade 基本上不会给你的应用增加任何负担。Blade 视图文件使用`.blade.php` 作为文件扩展名,被存放在 `resources/views` 目录。
## 模板继承
### 定义布局
首先,我们来研究一个「主」页面布局。因为大多数 `web` 应用会在不同的页面中使用相同的布局方式,因此可以很方便地定义单个 Blade 布局视图:
App Name - @yield('title')
@section('sidebar')
This is the master sidebar.
@show
@yield('content')
你可以看见,这段程序里包含常见的 HTML。但请注意 `@section` 和 `@yield` 和指令。如同 section 的意思,一个片段,`@section` 指令定义了片段的内容, 而 `@yield` 指令则用来显示片段的内容。
现在,我们已经定义好了这个应用程序的布局,接下来,我们定义一个继承此布局的子页面。
### 继承布局
在定义一个子视图时,使用 Blade 的 `@extends` 指令指定子视图要「继承」的视图。扩展自 Blade 布局的视图可以使用 `@section` 指令向布局片段注入内容。就如前面的示例中所示,这些片段的内容将由布局中的 `@yield` 指令控制显示:
@extends('layouts.app')
@section('title', 'Page Title')
@section('sidebar')
@parent
This is appended to the master sidebar.
@endsection
@section('content')
This is my body content.
@endsection
在这个示例中, `sidebar` 片段利用 `@parent` 指令向布局的 sidebar 追加(而非覆盖)内容。 在渲染视图时,@parent 指令将被布局中的内容替换。
> 技巧:和上一个示例相反,这里的 `sidebar` 片段使用 `@endsection` 代替 `@show` 来结尾。 `@endsection` 指令仅定义了一个片段, `@show` 则在定义的同时 立即 `yield` 这个片段。
`@yield` 指令还接受一个默认值作为第二个参数。如果被 「yield」的片段未定义,则该默认值被渲染:
@yield('content', View::make('view.name'))
Blade 视图可以用 `view` 辅助函数从路由中返回:
Route::get('blade', function () {
return view('child');
});
## 显示数据
你可以把变量置于花括号中以在视图中显示数据。 例如,给定下方的路由:
Route::get('greeting', function () {
return view('welcome', ['name' => 'Samantha']);
});
您可以像如下这样显示 `name` 变量的内容:
Hello, {{ $name }}.
> 技巧:Blade 的 `{{ }}` 语句将被 PHP 的 `htmlspecialchars` 函数自动转义以防范 XSS 攻击。
不仅可以显示传递给视图的变量的内容,您亦可输出任何 PHP 函数的结果。事实上,您可以在 Blade 模板的回显语句放置任何 PHP 代码:
The current UNIX timestamp is {{ time() }}.
#### 显示非转义字符
默认情况下, Blade `{{ }}` 语句将被 PHP 的 `htmlspecialchars` 函数自动转义以防范 XSS 攻击。如果您不想您的数据被转义,那么您可使用如下的语法:
Hello, {!! $name !!}.
> 注意:在应用中显示用户提供的数据时请格外小心,请尽可能的使用转义和双引号语法来防范 XSS 攻击。
#### 渲染 JSON
有时,为了初始化一个 JavaScript 变量,您可能会向视图传递一个数组并将其渲染成 JSON 。例如:
当然,您亦可使用 `@json` Blade 指令来代替手动调用 `json_encode` 方法。 `@json` 指令的参数和 PHP 的 `json_encode` 函数一致:
> 注意:使用 `@json` 指令时,您应该只渲染已经存在的变量为 JSON 。 Blade 模板是基于正则表达式的,如果尝试将一个复杂表达式传递给 `@json` 指令可能会导致无法预测的错误。
#### HTML 实体编码
默认情况下, Blade (以及 Laravel 的 `e` 辅助函数)将会对 HTML 实体进行双重编码。如果您想要禁用此举,您可以在 `AppServiceProvider` 中的 `boot` 方法中调用 `Blade::withoutDoubleEncoding` 方法:
### Blade & JavaScript 框架
由于许多 JavaScript 框架也使用「花括号」来标识将显示在浏览器中的表达式,因此,您可以使用 `@` 符号来表示 Blade 渲染引擎应当保持不变。例如:
Laravel
Hello, @{{ name }}.
在这个例子中, `@` 符号将被 Blade 移除;当然,Blade 将不会修改 `{{ name }}` 表达式,取而代之的是 JavaScript 模板来对其进行渲染。
`@` 符号也用于转义 Blade 指令:
{{-- Blade --}}
@@json()
@json()
#### `@verbatim` 指令
如果您在模板中显示很大一部分 JavaScript 变量,您可以将 HTML 嵌入到 `@verbatim` 指令中,这样,您就不需要在每一个 Blade 回显语句前添加 `@` 符号:
@verbatim
Hello, {{ name }}.
@endverbatim
## 控制结构
除了模板继承和显示数据以外, Blade 还为常见的 PHP 控制结构提供了便捷的快捷方式,例如条件语句和循环。这些快捷方式为 PHP 控制结构提供了一个非常清晰、简洁的书写方式,同时,还与 PHP 中的控制结构保持了相似的语法特性。
### If 语句
您可以使用 `@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` 指令:
@unless (Auth::check())
You are not signed in.
@endunless
除了已经讨论过了的条件指令外, `@isset` 和 `@empty` 指令亦可作为它们所对应的 PHP 函数的快捷方式:
@isset($records)
// $records 已经定义但不为空
@endisset
@empty($records)
// $records 为空……
@endempty
#### 鉴权指令
`@auth` 和 `@guest` 指令可用于快速判断当前用户是否已经获得授权或是当前用户是游客:
@auth
// 用户已经通过认证……
@endauth
@guest
// 用户没有通过认证……
@endguest
如有需要,您亦可在使用 `@auth` 和 `@guest` 指令时指定 [鉴权守卫](/docs/laravel/8.x/authentication):
@auth('admin')
// 用户已经通过认证
@endauth
@guest('admin')
// 用户没有通过认证……
@endguest
#### 区块指令
您可以使用 `@hasSection` 指令来判断区块是否有内容:
@hasSection('navigation')
@yield('navigation')
@endif
您可以使用 `sectionMissing` 指令来判断区块是否没有内容:
@sectionMissing('navigation')
@include('default-navigation')
@endif
#### 环境指令
您可以使用 `@production` 指令来判断应用是否处于生产环境:
@production
// 生产环境特定内容……
@endproduction
或者,您可以使用 `@env` 指令来判断应用是否运行于指定的环境:
@env('staging')
// 应用运行于「staging」环境……
@endenv
@env(['staging', 'production'])
// 应用运行于 「staging」环境或生产环境……
@endenv
### Switch 语句
您可使用 `@switch` , `@case` , `@break` , `@default` 和 `@endswitch` 语句来构造 Switch 语句:
@switch($i)
@case(1)
First case...
@break
@case(2)
Second case...
@break
@default
Default case...
@endswitch
### 循环
除了条件语句, Blade 还提供了与 PHP 循环结构功能相同的指令。同样,这些语句的功能和它们所对应的 PHP 语法一致:
@for ($i = 0; $i < 10; $i++)
The current value is {{ $i }}
@endfor
@foreach ($users as $user)
This is user {{ $user->id }}
@endforeach
@forelse ($users as $user)
{{ $user->name }}
@empty
No users
@endforelse
@while (true)
I'm looping forever.
@endwhile
> 技巧:循环时,您可以使用 [循环变量](#the-loop-variable) 去获取有关循环的有价值的信息,例如,您处于循环的第一个迭代亦或是处于最后一个迭代。
在使用循环的时候,您可以终止循环或跳过当前迭代:
@foreach ($users as $user)
@if ($user->type == 1)
@continue
@endif
{{ $user->name }}
@if ($user->number == 5)
@break
@endif
@endforeach
您可以在指令的单独一行中声明一个条件语句:
@foreach ($users as $user)
@continue($user->type == 1)
{{ $user->name }}
@break($user->number == 5)
@endforeach
### Loop 变量
循环时,循环内部可以使用 `$loop` 变量。该变量提供了访问一些诸如当前的循环索引和此次迭代是首次或是末次这样的信息的方式:
@foreach ($users as $user)
@if ($loop->first)
This is the first iteration.
@endif
@if ($loop->last)
This is the last iteration.
@endif
This is user {{ $user->id }}
@endforeach
如果您在嵌套循环中,您可以使用循环的 `$loop` 的变量的 `parent` 属性访问父级循环:
@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` 变量还包含各种各样有用的属性:
属性 | 描述
------------- | -------------
`$loop->index` | 当前迭代的索引(从 0 开始)。
`$loop->iteration` | 当前循环的迭代次数(从 1 开始)。
`$loop->remaining` | 循环剩余的迭代次数。
`$loop->count` | 被迭代的数组的元素个数。
`$loop->first` | 当前迭代是否是循环的首次迭代。
`$loop->last` | 当前迭代是否是循环的末次迭代。
`$loop->even` | 当前循环的迭代次数是否是偶数。
`$loop->odd` | 当前循环的迭代次数是否是奇数。
`$loop->depth` | 当前循环的嵌套深度。
`$loop->parent` | 嵌套循环中的父级循环。
### 注释
Blade 也允许您在视图中定义注释。但是,和 HTML 注释不同, Blade 注释不会被包含在应用返回的 HTML 中:
{{-- This comment will not be present in the rendered HTML --}}
### PHP
在许多情况下,嵌入 PHP 代码到您的视图中是很有用的。您可以在模板中使用 Blade 的 `@php` 指令执行原生的 PHP 代码块:
@php
//
@endphp
> 技巧:尽管 Blade 提供了这个功能,频繁使用它可能使得您的模板中嵌入过多的逻辑。
### `@once` 指令
`@once` 指令允许您定义模板的一部分内容,这部分内容在每一个渲染周期中只会被计算一次。该指令在使用 [堆栈](#stacks) 推送一段特定的 JavaScript 代码到页面的头部环境下是很有用的。例如,如果您想要在循环中渲染一个特定的 [组件](#components) ,您可能希望仅在组件渲染的首次推送 JavaScript 代码到头部:
@once
@push('scripts')
@endpush
@endonce
## 表单
### CSRF 域
任何您在应用中定义 HTML 表单的时候,您都应该在表单中包含一个隐藏的 CSRF token 域,这样一来, [ CSRF 保护](https://laravel.com/docs/laravel/8.x/csrf) 中间件便能校验请求。您可以使用 `@csrf` Blade 指令来生成一个 token 域:
### 方法域
由于 HTML 表单不能够构造 `PUT` , `PATCH` 或 `DELETE` 请求,您需要添加隐藏的 `_method` 域来模拟这些 HTTP 动作。您亦可使用 `@method` Blade 指令来创建这个方法域:
### 校验错误
`@error` 指令可用于快速检查指定属性是否存在 [校验错误信息](/docs/laravel/8.x/validation#quick-displaying-the-validation-errors) 。在 `@error` 指令中,您可以回显 `$message` 变量来显示错误信息:
Post Title
@error('title')
{{ $message }}
@enderror
您可以将 [指定错误包的名称](/docs/laravel/8.x/validation#named-error-bags) 作为 `@error` 指令的第二个参数来检索含有多个表单的页面的验证错误信息:
Email address
@error('email', 'login')
{{ $message }}
@enderror
## 组件
组件和插槽的作用与区块和布局的作用一致;不过,有些人可能觉着组件和插槽更易于理解。有两种书写组件的方法:基于类的组件和匿名组件。
您可以使用 `make:component` Artisan 命令来创建一个基于类的组件。我们将会创建一个简单的 `Alert` 组件用于说明如何使用组件。`make:component` 命令将会把组件置于 `App\View\Components` 目录中:
php artisan make:component Alert
`make:component` 命令将会为组件创建一个视图模板。创建的视图被置于 `resources/views/components` 目录中。
#### 手动注册包组件
当为您自己的应用编写组件的时候,Laravel 将会自动发现位于 `app/View/Components` 目录和 `resources/views/components` 目录中的组件。
当然,如果您使用 Blade 组件编译一个包,您可能需要手动注册组件类及其 HTML 标签别名。您应该在包的服务提供者的 `boot` 方法中注册您的组件:
use Illuminate\Support\Facades\Blade;
use App\View\Components\Alert;
/**
* 驱动您的包的服务
*/
public function boot()
{
Blade::component('package-alert', Alert::class);
}
当组件注册完成后,便可使用标签别名来对其进行渲染。
### 显示组件
要显示一个组件,您可以在 Blade 模板中使用 Blade 组件标签。 Blade 组件以 `x-` 字符串开始,其后紧接组件类 kebab case 形式的名称(即单词与单词之间使用短横线 `-` 进行连接):
如果组件位于 `App\View\Components` 目录的子目录中,您可以使用 `.` 字符来指定目录层级。例如,假设我们有一个组件位于 `App\View\Components\Inputs\Button.php`,那么我们可以像这样渲染它:
### 传递数据到组件中
您可以使用 HTML 属性传递数据到 Blade 组件中。普通的值可以通过简单的 HTML 属性来传递给组件。PHP 表达式和变量应该通过以 `:` 字符作为前缀的变量来进行传递:
您应该在类的构造器中定义组件的必要数据。在组件的视图中,组件的所有 public 类型的属性都是可用的。不必通过组件类的 `render` 方法传递:
type = $type;
$this->message = $message;
}
/**
* 获取组件的视图 / 内容
*
* @return \Illuminate\View\View|\Closure|string
*/
public function render()
{
return view('components.alert');
}
}
渲染组件时,您可以回显变量名来显示组件的 public 变量的内容:
{{ $message }}
#### 命名方式(Casing)
组件的构造器的参数应该使用 `驼峰式` 类型,在 HTML 属性中引用参数名时应该使用 `烤串式` 类型(kebab-case :单词与单词之间使用短横线 `-` 进行连接)。例如,给定如下的组件构造器:
/**
* 创建一个组件实例
*
* @param string $alertType
* @return void
*/
public function __construct($alertType)
{
$this->alertType = $alertType;
}
`$alertType` 参数可以像这样使用:
#### 组件方法
除了在组件模板中可以使用 public 类型的变量以外,任何 public 类型的方法亦可在模板中执行。例如,假设某组件拥有 `isSelected` 方法:
/**
* 判断给定的选项是否是当前选中的选项
*
* @param string $option
* @return bool
*/
public function isSelected($option)
{
return $option === $this->selected;
}
您可以通过调用与方法名称相同的变量名来执行这个方法:
{{ $label }}
#### 在类中使用属性 & 插槽
Blade 组件也允许您在类的渲染方法中访问组件的名称,属性以及插槽。当然,为了访问这个数据,您应该在组件的 `render` 方法中返回一个闭包函数。这个闭包函数接收一个名为 `$data` 的数组作为它唯一的参数:
/**
* 获取组件的视图 / 内容
*
* @return \Illuminate\View\View|\Closure|string
*/
public function render()
{
return function (array $data) {
// $data['componentName'];
// $data['attributes'];
// $data['slot'];
return 'Component content
';
};
}
`componentName` 等于使用 `x-` 作为前缀后 HTML 标签中使用的名称。 `attributes` 元素包含所有可能出现在 HTML 标签中的属性。 `slot` 元素是一个 `Illuminate\Support\HtmlString` 实例,该实例包含组件中的插槽定义的内容。
#### 附加依赖
如果您的组件需要使用 [服务容器](/docs/laravel/8.x/container) 中的依赖,您可以在组件所有数据属性前将其列出,服务容器会自动将其注入:
use App\Services\AlertCreator
/**
* 创建一个组件实例。
*
* @param \App\Services\AlertCreator $creator
* @param string $type
* @param string $message
* @return void
*/
public function __construct(AlertCreator $creator, $type, $message)
{
$this->creator = $creator;
$this->type = $type;
$this->message = $message;
}
### 管理属性
我们已经讨论了如何通过属性传递数据给组件;然而,有时您可能需要指定一个诸如 `class` 的额外的 HTML 属性,这些属性不是组件所必须的数据。此时,您可能想要将这些可选的属性向下传递到组件模板的根元素中。例如,我们想要像这样渲染一个 `alert` 组件:
所有不属于组件的构造器的属性都将被自动添加到组件的「属性包」中。该属性包将通过 `$attributes` 变量自动传递给组件。您可以通过回显这个变量来渲染所有的属性:
> 注意:此时不支持在组件中使用诸如 `@env` 这样的指令。
#### 默认 / 合并属性
某些时候,你可能需要指定属性的默认值,或将其他值合并到组件的某些属性中。为此,你可以使用属性包的 `merge` 方法:
merge(['class' => 'alert alert-'.$type]) }}>
{{ $message }}
假设我们如下方所示使用该组件:
最终呈现的组件 HTML 将如下所示:
#### 过滤属性
你可以使用 `filter` 方法过滤属性。如果你想在属性包中保留属性,这个方法接受一个应该返回 `true` 的闭包:
{{ $attributes->filter(fn ($value, $key) => $key == 'foo') }}
为了方便,你也可以使用 `whereStartsWith` 方法来检索所有以特定字符串开头的键所对应的属性:
{{ $attributes->whereStartsWith('wire:model') }}
使用 `first` 方法,你可以渲染一个属性包中的第一个属性:
{{ $attributes->whereStartsWith('wire:model')->first() }}
### 插槽
通常,你需要通过 「slots」 向组件传递附加内容。 假设我们创建的 `alert` 组件具有以下标记:
{{ $slot }}
我门可以通过向组件注入内容的方式,将内容传递到 `slot` :
Whoops! Something went wrong!
有时候一个组件可能需要在它内部的不同位置放置多个不同的插槽。我们来修改一下 alert 组件,使其允许注入 「title」:
{{ $title }}
{{ $slot }}
您可以使用 `x-slot` 标签来定义命名插槽的内容。任何没有在 `x-slot` 标签中的内容都将传递给 `$slot` 变量中的组件:
Server Error
Whoops! Something went wrong!
#### 作用域插槽
如果您使用诸如 Vue 这样的 JavaScript 框架,那么您应该很熟悉「作用域插槽」,它允许您从插槽中的组件访问数据或者方法。 Laravel 中也有类似的用法,只需在您的组件中定义 public 方法或属性,并且使用 `$component` 变量来访问插槽中的组件:
{{ $component->formatAlert('Server Error') }}
Whoops! Something went wrong!
### 内联组件视图
对于小型组件而言,管理组件类和组件视图模板可能会很麻烦。因此,您可以从 `render` 方法中返回组件的内容:
/**
* 获取组件的视图 / 内容。
*
* @return \Illuminate\View\View|\Closure|string
*/
public function render()
{
return <<<'blade'
{{ $slot }}
blade;
}
#### 生成内联视图组件
要创建一个渲染内联视图的组件,您可以在运行 `make:component` 命令时使用 `inline` 选项:
php artisan make:component Alert --inline
### 匿名组件
与行内组件相同,匿名组件提供了一个通过单个文件管理组件的机制。然而,匿名组件使用的是一个没有关联类的单一视图文件。要定义一个匿名组件,您只需将 Blade 模板置于 `resources/views/components` 目录下。例如,假设您在 `resources/views/components/alert.blade.php` 中定义了一个组件:
如果组件在 `components` 目录的子目录中,您可以使用 `.` 字符来指定其路径。例如,假设组件被定义在 `resources/views/components/inputs/button.blade.php` 中,您可以像这样渲染它:
#### 数据 / 属性
由于匿名组件没有任何关联类,您可能想要区分哪些数据应该被作为变量传递给组件,而哪些属性应该被存放于 [属性包](#managing-attributes) 中。
您可以在组件的 Blade 模板的顶层使用 `@props` 指令来指定哪些属性应该作为数据变量。组件中的其他属性都将通过属性包的形式提供。如果您想要为某个数据变量指定一个默认值,您可以将属性名作为数组键,默认值作为数组值来实现:
@props(['type' => 'info', 'message'])
merge(['class' => 'alert alert-'.$type]) }}>
{{ $message }}
### 动态组件
有时,您可能需要渲染一个组件,但在运行前不知道要渲染哪一个。这种情况下,您可以使用 Laravel 内置的 `dynamic-component` 组件来渲染一个基于值或变量的组件:
## 引入子视图
Blade 的 `@include` 指令可用于从另一个视图包含一个 Blade 视图。子视图将继承父视图中所有可用的变量:
@include('shared.errors')
除了子视图继承父视图中所有可用的数据,您亦可通过数组将数据传递给子视图:
@include('view.name', ['some' => 'data'])
如果您尝试 `@include` 一个不存在的视图,Laravel 将会报错。如果您想要包含一个不确定是否存在的视图时,您可以使用 `@includeIf` 指令:
@includeIf('view.name', ['some' => 'data'])
如果想要在某个表达式的值计算为 `true` 时 `@include` 一个视图,您可以使用 `@includeWhen` 指令:
@includeWhen($boolean, 'view.name', ['some' => 'data'])
如果想要在某个表达式的值计算为 `false` 时 `@include` 一个视图,您可以使用 `@includeUnless` 指令:
@includeUnless($boolean, 'view.name', ['some' => 'data'])
要包含指定的视图数组中存在的第一个视图,可以使用 `includeFirst` 指令:
@includeFirst(['custom.admin', 'admin'], ['some' => 'data'])
> 注意:您应该避免在 Blade 视图中使用 `__DIR__` 和 `__FILE__` 常量,因为它们会引用本地已经缓存了的,编译过了的视图的路径。
#### 给子视图取别名
如果您的 Blade 包含了一个存储于子目录中的视图,您可能希望给它们取一个别名以方便访问。例如,假设存储于 `resources/views/includes/input.blade.php` 的视图拥有如下内容:
您可以使用 `include` 为 `includes.input` 取一个名为 `input` 的别名。通常情况下,这需要在 `AppServiceProvider` 中的 `boot` 方法中完成:
use Illuminate\Support\Facades\Blade;
Blade::include('includes.input', 'input');
一旦您为子视图取了别名,您便可在 Blade 指令中使用别名来对其进行渲染:
@input(['type' => 'email'])
### 为集合渲染视图
您可以使用 Blade 的 `@each` 指令将循环合并在一行内:
@each('view.name', $jobs, 'job')
第一个参数是数组或集合中的元素的要渲染的视图片段。第二个参数是您想要迭代的数组或集合,当第三个参数是一个表示当前迭代的视图的变量名。因此,如果您遍历一个名为 `jobs` 的数组,通常会在视图片段中使用 `job` 变量来访问每一个 job (jobs 数组的元素)。在您的视图片段中,可以使用 `key` 变量来访问当前迭代的键。
您亦可传递第四个参数给 `@each` 指令。当给定的数组为空时,将会渲染该参数所对应的视图。
@each('view.name', $jobs, 'job', 'view.empty')
> 注意:通过 `@each` 指令渲染的视图不会继承父视图的变量。如果子视图需要使用这些变量,您可以使用 `@foreach` 和 `@include` 来代替它。
## 视图堆栈
Blade 允许您将视图推送到堆栈中,它们可以在其他视图或布局中进行渲染。这在子视图中需要指定 JavaScript 库时非常有用:
@push('scripts')
@endpush
您亦可按需进行多次推送。请 `@stack` 指令来完整堆栈内容的渲染:
@stack('scripts')
如果您想要预置内容于栈顶,您可以使用 `@prepend` 指令:
@push('scripts')
This will be second...
@endpush
// Later...
@prepend('scripts')
This will be first...
@endprepend
## 服务注入
`@inject` 指令可用于从 Laravel 的 [服务容器](/docs/laravel/8.x/container) 中检索服务。传递给 `@inject` 的第一个参数是将要置入的服务的变量名,第二个参数可以是您想要解析的类名或接口名:
@inject('metrics', 'App\Services\MetricsService')
Monthly Revenue: {{ $metrics->monthlyRevenue() }}.
## 扩展 Blade
Blade 允许您使用 `directive` 方法来自定义指令。当 Blade 编译器遇到自定义指令时,将会调用其包含的表达式提供的回调。
下方的例子创建了一个 `@datetime($var)` 指令,其用于格式化指定的 `DateTime` 实例 `$var`:
format('m/d/Y H:i'); ?>";
});
}
}
如您所见,我们将在任何被传递给该指令的表达式中链式调用 `format` 方法。因此,在这个例子中,最终该指令生成的 PHP 代码如下:
format('m/d/Y H:i'); ?>
> 注意:在更新了 Blade 指令的逻辑后,您需要删除所有的 Blade 视图缓存。您可以通过 `view:clear` Artisan 命令来实现。
### 自定义 If 语句
在定义一个简单的自定义的条件语句时,编写自定义指令可能会比必要的步骤还要复杂。因此, Blade 提供了一个 `Blade::if` 方法,它允许您使用闭包快速自定义一个条件指令。例如,让我们自定义一个检查当前应用的云服务商的条件指令。我们可以在 `AppServiceProvider` 中的 `boot` 方法中这样做:
use Illuminate\Support\Facades\Blade;
/**
* 驱动应用服务。
*
* @return void
*/
public function boot()
{
Blade::if('cloud', function ($provider) {
return config('filesystems.default') === $provider;
});
}
当自定义条件语句完成定义后,我们可以在模板中方便地使用它们:
@cloud('digitalocean')
// 应用使用 digitalocean 云服务商……
@elsecloud('aws')
// 应用使用 aws 云服务商……
@else
// 应用没有使用 digitalocean 亦没有使用 aws 提供的云服务……
@endcloud
@unlesscloud('aws')
// 应用没有使用 aws 提供的云服务……
@endcloud