Blade 模板

介绍

Blade 是 Laravel 内置的简单但强大的模板引擎。与一些 PHP 模板引擎不同,Blade 并不限制你在模板中使用纯 PHP 代码。事实上,所有 Blade 模板都会被编译成普通的 PHP 代码,并缓存直到它们被修改,这意味着 Blade 对你的应用几乎没有额外开销。Blade 模板文件使用 .blade.php 文件扩展名,通常存储在 resources/views 目录中。

Blade 视图可以通过全局的 view 辅助函数从路由或控制器中返回。当然,如同视图文档中所提到的,可以通过 view 辅助函数的第二个参数将数据传递给 Blade 视图:

Route::get('/', function () {
    return view('greeting', ['name' => 'Finn']);
});

使用 Livewire 超级增强 Blade

想要将你的 Blade 模板提升到一个新的高度,并轻松构建动态界面吗?那就看看 【Laravel Livewire】。Livewire 允许你编写增强了动态功能的 Blade 组件,这些功能通常只有通过像 React 或 Vue 这样的前端框架才能实现。Livewire 提供了一种构建现代响应式前端的绝佳方法,无需使用许多 JavaScript 框架的复杂性、客户端渲染或构建步骤。

显示数据

你可以通过将变量放在大括号中来显示传递给 Blade 视图的数据。例如,给定以下路由:

Route::get('/', function () {
    return view('welcome', ['name' => 'Samantha']);
});

你可以像这样显示 name 变量的内容:

Hello, {{ $name }}.

Blade 的 {{ }} 输出语句会自动通过 PHP 的 htmlspecialchars 函数处理,以防止 XSS 攻击。

你不仅仅限于显示传递给视图的变量内容,还可以回显任何 PHP 函数的结果。实际上,你可以在 Blade 的输出语句中放入任何你想要的 PHP 代码:

The current UNIX timestamp is {{ time() }}.

HTML 实体编码

默认情况下,Blade(以及 Laravel 的 e 函数)会对 HTML 实体进行双重编码。如果你想禁用双重编码,可以在 AppServiceProviderboot 方法中调用 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();
    }
}

显示未转义的数据

默认情况下,Blade 的 {{ }} 语句会自动通过 PHP 的 htmlspecialchars 函数处理,以防止 XSS 攻击。如果你不希望数据被转义,可以使用以下语法:

Hello, {!! $name !!}.

在回显用户提供的内容时,要非常小心。通常,应该使用转义的双大括号语法来防止 XSS 攻击,尤其是在显示用户提供的数据时。

Blade 与 JavaScript 框架

由于许多 JavaScript 框架也使用 “花括号” 来表示一个表达式应该在浏览器中显示,您可以使用 @ 符号来告诉 Blade 渲染引擎某个表达式应该保持原样。例如:

<h1>Laravel</h1>

Hello, @{{ name }}.

在这个例子中,@ 符号会被 Blade 移除;然而,{{ name }} 表达式将不会被 Blade 引擎处理,它将被 JavaScript 框架渲染。

@ 符号也可以用来转义 Blade 指令:

{{-- Blade 模板 --}}
@@if()

<!-- HTML 输出 -->
@if()

渲染 JSON

有时,您可能会将一个数组传递给视图,并希望将其作为 JSON 渲染,以便初始化一个 JavaScript 变量。例如:

<script>
    var app = <?php echo json_encode($array); ?>;
</script>

然而,您可以使用 Illuminate\Support\Js::from 方法指令,而无需手动调用 json_encodefrom 方法接受与 PHP 的 json_encode 函数相同的参数;但是,它会确保生成的 JSON 被正确转义,以便可以安全地包含在 HTML 引号中。from 方法会返回一个 JavaScript JSON.parse 语句,将给定的对象或数组转换为有效的 JavaScript 对象:

<script>
    var app = {{ Illuminate\Support\Js::from($array) }};
</script>

最新版本的 Laravel 应用程序框架包括一个 Js facade,提供了在 Blade 模板中方便访问该功能:

<script>
    var app = {{ Js::from($array) }};
</script>

您应该仅使用 Js::from 方法来渲染已存在的变量为 JSON。由于 Blade 模板是基于正则表达式的,因此将复杂的表达式传递给指令可能会导致意外的错误。

@verbatim 指令

如果您在模板中显示 JavaScript 变量的部分内容较大,可以使用 @verbatim 指令将 HTML 包裹起来,这样您就不必在每个 Blade echo 语句前加上 @ 符号:

@verbatim
    <div class="container">
        Hello, {{ name }}.
    </div>
@endverbatim

Blade 指令

除了模板继承和数据显示,Blade 还提供了常见 PHP 控制结构的便捷快捷方式,如条件语句和循环语句。这些快捷方式为处理 PHP 控制结构提供了一种非常简洁的方式,同时仍保持与 PHP 原生语法的相似性。

If 语句

您可以使用 @if@elseif@else@endif 指令构建 if 语句。这些指令的功能与 PHP 中的对应语法相同:

@if (count($records) === 1)
    我有一条记录!
@elseif (count($records) > 1)
    我有多条记录!
@else
    我没有任何记录!
@endif

为了方便,Blade 还提供了 @unless 指令:

@unless (Auth::check())
    您没有登录。
@endunless

除了已经讨论过的条件指令外,@isset@empty 指令也可以作为 PHP 函数的便捷快捷方式使用:

@isset($records)
    // $records 已定义且不为 null...
@endisset

@empty($records)
    // $records 是“空”...
@endempty

身份认证指令

@auth@guest 指令可用于快速判断当前用户是否已【认证】或是访客:

@auth
    // 用户已认证...
@endauth

@guest
    // 用户未认证...
@endguest

如果需要,您可以指定在使用 @auth@guest 指令时检查的认证守卫:

@auth('admin')
    // 用户已认证...
@endauth

@guest('admin')
    // 用户未认证...
@endguest

环境指令

您可以使用 @production 指令检查应用是否在生产环境中运行:

@production
    // 生产环境特定内容...
@endproduction

或者,您可以使用 @env 指令来确定应用是否在特定环境中运行:

@env('staging')
    // 应用在“staging”环境中运行...
@endenv

@env(['staging', 'production'])
    // 应用在“staging”或“production”环境中运行...
@endenv

区块指令

您可以使用 @hasSection 指令来判断模板继承的某个区块是否有内容:

@hasSection('navigation')
    <div class="pull-right">
        @yield('navigation')
    </div>

    <div class="clearfix"></div>
@endif

您也可以使用 @sectionMissing 指令来判断某个区块是否没有内容:

@sectionMissing('navigation')
    <div class="pull-right">
        @include('default-navigation')
    </div>
@endif

会话指令

@session 指令可用于判断某个会话值是否存在。如果会话值存在,则会评估 @session@endsession 指令之间的模板内容。在 @session 指令的内容中,您可以回显 $value 变量来显示会话值:

@session('status')
    <div class="p-4 bg-green-100">
        {{ $value }}
    </div>
@endsession

Switch 语句

Switch 语句可以使用 @switch@case@break@default@endswitch 指令构建:

@switch($i)
    @case(1)
        第一个情况...
        @break

    @case(2)
        第二个情况...
        @break

    @default
        默认情况...
@endswitch

循环

除了条件语句,Blade 还提供了简单的指令来处理 PHP 的循环结构。每个指令的功能与 PHP 中的相应结构完全相同:

@for ($i = 0; $i < 10; $i++)
    当前值是 {{ $i }}
@endfor

@foreach ($users as $user)
    <p>这是用户 {{ $user->id }}</p>
@endforeach

@forelse ($users as $user)
    <li>{{ $user->name }}</li>
@empty
    <p>没有用户</p>
@endforelse

@while (true)
    <p>我正在循环</p>
@endwhile

在使用 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

循环变量

foreach 循环中,您可以使用 $loop 变量来获取一些有用的信息,比如当前的循环索引,以及是否是第一次或最后一次迭代:

@foreach ($users as $user)
    @if ($loop->first)
        这是第一次迭代。
    @endif

    @if ($loop->last)
        这是最后一次迭代。
    @endif

    <p>这是用户 {{ $user->id }}</p>
@endforeach

如果您处于嵌套循环中,可以通过 parent 属性访问父循环的 $loop 变量:

@foreach ($users as $user)
    @foreach ($user->posts as $post)
        @if ($loop->parent->first)
            这是父循环的第一次迭代。
        @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 指令用于条件性地编译一个 CSS 类字符串。该指令接受一个类数组,其中数组的键表示您希望添加的类或类列表,而值是一个布尔表达式。如果数组元素的键是数字,它将始终包含在渲染的类列表中:

@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 指令可用于有条件地向 HTML 元素添加内联 CSS 样式:

@php
    $isActive = true;
@endphp

<span @style([
    'background-color: red',
    'font-weight: bold' => $isActive,
])></span>

<span style="background-color: red; font-weight: bold;"></span>

附加属性

为了方便起见,您可以使用 @checked 指令轻松地指示某个 HTML 复选框输入是否被选中。该指令将在提供的条件为 true 时输出 checked

<input
    type="checkbox"
    name="active"
    value="active"
    @checked(old('active', $user->active))
/>

同样,@selected 指令可用于指示某个选项是否应该被选中:

<select name="version">
    @foreach ($product->versions as $version)
        <option value="{{ $version }}" @selected(old('version') == $version)>
            {{ $version }}
        </option>
    @endforeach
</select>

此外,@disabled 指令可用于指示某个元素是否应该被禁用:

<button type="submit" @disabled($errors->isNotEmpty())>Submit</button>

此外,@readonly 指令可用于指示某个元素是否应该是只读的:

<input
    type="email"
    name="email"
    value="email@laravel.com"
    @readonly($user->isNotAdmin())
/>

另外,@required 指令可用于指示某个元素是否应该是必填的:

<input
    type="text"
    name="title"
    value="title"
    @required($user->isAdmin())
/>

包含子视图

虽然您可以使用 @include 指令,Blade 【组件】提供了类似的功能,并且比 @include 指令具有更多优点,如数据和属性绑定。

Blade 的 @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'])

您应该避免在 Blade 视图中使用 __DIR____FILE__ 常量,因为它们会引用缓存编译视图的位置。

渲染集合的视图

您可以将循环和包含结合成一行,使用 Blade 的 @each 指令:

@each('view.name', $jobs, 'job')

@each 指令的第一个参数是要为数组或集合中的每个元素渲染的视图。第二个参数是您希望迭代的数组或集合,而第三个参数是将为当前迭代分配的变量名。因此,例如,如果您正在遍历一组工作,通常您会希望在视图中将每个工作作为 job 变量访问。当前迭代的数组键将作为 key 变量在视图中可用。

您还可以传递第四个参数给 @each 指令。此参数决定如果给定的数组为空时应渲染哪个视图:

@each('view.name', $jobs, 'job', 'view.empty')

通过 @each 渲染的视图不会继承父视图的变量。如果子视图需要这些变量,您应该使用 @foreach@include 指令代替。

@once 指令

@once 指令允许您定义一个仅在每次渲染周期中评估一次的模板部分。这在使用【堆栈】将特定的 JavaScript 推送到页面头部时非常有用。例如,如果您在循环中渲染某个【组件】,您可能希望仅在第一次渲染该组件时将 JavaScript 推送到头部:

@once
    @push('scripts')
        <script>
            // 您的自定义 JavaScript...
        </script>
    @endpush
@endonce

由于 @once 指令通常与 @push@prepend 指令一起使用,Laravel 还提供了 @pushOnce@prependOnce 指令,方便您使用:

@pushOnce('scripts')
    <script>
        // 您的自定义 JavaScript...
    </script>
@endPushOnce

原生 PHP

在某些情况下,将 PHP 代码嵌入到视图中是很有用的。您可以使用 Blade 的 @php 指令在模板中执行一段纯 PHP 代码:

@php
    $counter = 1;
@endphp

或者,如果您只需要使用 PHP 导入一个类,您可以使用 @use 指令:

@use('App\Models\Flight')

您还可以为 @use 指令提供第二个参数,以便为导入的类起一个别名:

@use('App\Models\Flight', 'FlightModel')

注释

Blade 还允许您在视图中定义注释。不过,与 HTML 注释不同,Blade 注释不会包含在您的应用程序返回的 HTML 中:

{{-- 这个注释不会出现在渲染后的 HTML 中 --}}

组件

组件和插槽提供了与部分(section)、布局和包含(include)相似的好处;然而,有些人可能会发现组件和插槽的思维模型更容易理解。编写组件有两种方式:基于类的组件和匿名组件

基于类的组件

要创建一个基于类的组件,您可以使用 make:component Artisan 命令。为了说明如何使用组件,我们将创建一个简单的 Alert 组件。make:component 命令会将组件放在 app/View/Components 目录下:

php artisan make:component Alert

make:component 命令还会为组件创建一个视图模板。该视图将被放置在 resources/views/components 目录中。在为您的应用程序编写组件时,组件会自动在 app/View/Components 目录和 resources/views/components 目录下被发现,因此通常不需要进一步的组件注册。

您还可以在子目录中创建组件:

php artisan make:component Forms/Input

上述命令将在 app/View/Components/Forms 目录中创建一个 Input 组件,并且视图文件将被放置在 resources/views/components/forms 目录。

匿名组件

如果您只需要一个 Blade 模板而没有类的组件(即匿名组件),您可以在调用 make:component 命令时使用 --view 标志:

php artisan make:component forms.input --view

上述命令将在 resources/views/components/forms/input.blade.php 创建一个 Blade 文件,您可以通过 <x-forms.input /> 渲染它作为组件。

手动注册包组件

当您为自己的应用程序编写组件时,组件会自动在 app/View/Components 目录和 resources/views/components 目录下被发现。

然而,如果您正在构建一个使用 Blade 组件的包,您需要手动注册组件类及其 HTML 标签别名。通常,您应该在包的服务提供者的 boot 方法中注册您的组件:

use Illuminate\Support\Facades\Blade;

public function boot(): void
{
    Blade::component('package-alert', Alert::class);
}

注册后,您可以通过其标签别名来渲染组件:

<x-package-alert/>

或者,您可以使用 componentNamespace 方法按照约定自动加载组件类。例如,Nightshade 包可能有 CalendarColorPicker 组件,它们位于 Package\Views\Components 命名空间:

use Illuminate\Support\Facades\Blade;

public function boot(): void
{
    Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
}

这将允许通过包命名空间使用包组件,使用 package-name:: 语法:

<x-nightshade::calendar />
<x-nightshade::color-picker />

Blade 会自动通过 Pascal 大小写法来检测与该组件关联的类。子目录也支持使用 “点” 符号表示法。

渲染组件

要在 Blade 模板中显示一个组件,您可以使用 Blade 组件标签。Blade 组件标签以 x- 开头,后跟组件类的 kebab-case 名称:

<x-alert/>

<x-user-profile/>

如果组件类位于 app/View/Components 目录中的子目录下,您可以使用 . 字符表示目录的嵌套。例如,如果组件位于 app/View/Components/Inputs/Button.php,则可以这样渲染它:

<x-inputs.button/>

如果您希望条件渲染组件,可以在组件类中定义一个 shouldRender 方法。如果该方法返回 false,组件将不会被渲染:

use Illuminate\Support\Str;

public function shouldRender(): bool
{
    return Str::length($this->message) > 0;
}

索引组件

有时候,组件是某个组件组的一部分,您可能希望将相关组件放在同一个目录中。例如,假设有一个 “card” 组件,其类结构如下:

`App\Views\Components\Card\Card`
`App\Views\Components\Card\Header`
`App\Views\Components\Card\Body`

由于根组件 Card 被嵌套在 Card 目录中,您可能会认为需要通过 <x-card.card> 来渲染该组件。然而,当一个组件的文件名与其目录名匹配时,Laravel 会自动认为该组件是 “根” 组件,并允许您在渲染组件时无需重复目录名称:

<x-card>
    <x-card.header>...</x-card.header>
    <x-card.body>...</x-card.body>
</x-card>

传递数据给组件

您可以通过 HTML 属性将数据传递给 Blade 组件。硬编码的原始值可以通过简单的 HTML 属性字符串传递给组件,而 PHP 表达式和变量则应通过以 : 字符为前缀的属性传递给组件:

<x-alert type="error" :message="$message"/>

您应在组件类的构造函数中定义所有组件的数据属性。组件中的所有公共属性将自动在组件的视图中可用,因此不需要从组件的 render 方法将数据传递给视图:

<?php

namespace App\View\Components;

use Illuminate\View\Component;
use Illuminate\View\View;

class Alert extends Component
{
    /**
     * Create the component instance.
     */
    public function __construct(
        public string $type,
        public string $message,
    ) {}

    /**
     * Get the view / contents that represent the component.
     */
    public function render(): View
    {
        return view('components.alert');
    }
}

当组件被渲染时,您可以通过按名称回显组件公共变量的值:

<div class="alert alert-{{ $type }}">
    {{ $message }}
</div>

大小写规范

组件构造函数的参数应使用 camelCase(驼峰命名法),而在 HTML 属性中引用参数时应使用 kebab-case(短横线命名法)。例如,假设以下是组件的构造函数:

/**
 * Create the component instance.
 */
public function __construct(
    public string $alertType,
) {}

$alertType 参数可以像下面这样传递给组件:

<x-alert alert-type="danger" />

简化属性语法

在将属性传递给组件时,您还可以使用 “简短属性” 语法。这通常很方便,因为属性名称通常与它们对应的变量名称相匹配:

{{-- 简短属性语法... --}}
<x-profile :$userId :$name />

{{-- 等价于... --}}
<x-profile :user-id="$userId" :name="$name" />

转义属性渲染

由于一些 JavaScript 框架(如 Alpine.js)也使用以冒号 : 为前缀的属性,您可以使用双冒号 :: 前缀来告知 Blade 该属性不是 PHP 表达式。例如,给定以下组件:

<x-button ::class="{ danger: isDeleting }">
    Submit
</x-button>

Blade 渲染的 HTML 将是:

<button :class="{ danger: isDeleting }">
    Submit
</button>

组件方法

除了公共变量可以在组件模板中使用外,组件中的任何公共方法也可以被调用。例如,假设组件有一个 isSelected 方法:

/**
 * Determine if the given option is the currently selected option.
 */
public function isSelected(string $option): bool
{
    return $option === $this->selected;
}

您可以通过调用与方法名称匹配的变量在组件模板中执行此方法:

<option {{ $isSelected($value) ? 'selected' : '' }} value="{{ $value }}">
    {{ $label }}
</option>

访问组件类中的属性和插槽

Blade 组件还允许您在类的 render 方法中访问组件名称、属性和插槽。然而,为了访问这些数据,您应在组件的 render 方法中返回一个闭包:

use Closure;

/**
 * Get the view / contents that represent the component.
 */
public function render(): Closure
{
    return function () {
        return '<div {{ $attributes }}>Components content</div>';
    };
}

render 方法返回的闭包还可以接收一个 $data 数组作为其唯一参数。该数组将包含提供有关组件的若干元素:

return function (array $data) {
    // $data['componentName'];
    // $data['attributes'];
    // $data['slot'];

    return '<div {{ $attributes }}>Components content</div>';
}

$data 数组中的元素不应直接嵌入到你的 render 方法返回的 Blade 字符串中,因为这样做可能会通过恶意的属性内容允许远程代码执行。

componentName 等于 HTML 标签中 x- 前缀后使用的名称。因此,<x-alert />componentName 将是 alertattributes 元素将包含 HTML 标签中存在的所有属性。slot 元素是一个 Illuminate\Support\HtmlString 实例,包含组件插槽的内容。

闭包应返回一个字符串。如果返回的字符串对应一个现有的视图,该视图将被渲染;否则,返回的字符串将作为内联 Blade 视图进行评估。

额外依赖

如果您的组件需要来自 Laravel 【服务容器】的依赖,您可以在任何组件的数据属性之前列出它们,容器将自动注入这些依赖:

use App\Services\AlertCreator;

/**
 * Create the component instance.
 */
public function __construct(
    public AlertCreator $creator,
    public string $type,
    public string $message,
) {}

隐藏属性/方法

如果您希望防止某些公共方法或属性作为变量暴露到组件模板中,您可以将它们添加到组件的 $except 数组属性中:

<?php

namespace App\View\Components;

use Illuminate\View\Component;

class Alert extends Component
{
    /**
     * The properties / methods that should not be exposed to the component template.
     *
     * @var array
     */
    protected $except = ['type'];

    /**
     * Create the component instance.
     */
    public function __construct(
        public string $type,
    ) {}
}

组件属性

我们已经讨论了如何将数据属性传递给组件;然而,有时您可能需要指定其它 HTML 属性,如 class,这些属性并不是组件功能所必需的。通常,您希望将这些额外的属性传递给组件模板的根元素。例如,假设我们要渲染一个 alert 组件,像这样:

<x-alert type="error" :message="$message" class="mt-4"/>

所有不属于组件构造函数的一部分的属性将自动添加到组件的 “属性袋” 中。这个属性袋通过 $attributes 变量自动提供给组件。您可以通过回显该变量来渲染所有属性:

<div {{ $attributes }}>
    <!-- 组件内容 -->
</div>

目前,像 @env 这样的指令在组件标签中是不支持的。例如,<x-alert :live="@env('production')"/> 不会被编译。

默认值 / 合并属性

有时,您可能需要为属性指定默认值,或将额外的值合并到某些组件属性中。为此,您可以使用属性袋的 merge 方法。这个方法特别适用于定义一组始终应用于组件的默认 CSS 类:

<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">
    <!-- $message 变量的内容 -->
</div>

条件合并类

有时您可能希望在某些条件为 true 时合并类。您可以通过 class 方法实现,这个方法接受一个类数组,其中数组的键包含您希望添加的类,而值是一个布尔表达式。如果数组元素具有数字键,则它将始终包含在渲染的类列表中:

<div {{ $attributes->class(['p-4', 'bg-red' => $hasError]) }}>
    {{ $message }}
</div>

如果您需要将其它属性合并到您的组件中,您可以将 merge 方法链式调用到 class 方法上:

<button {{ $attributes->class(['p-4'])->merge(['type' => 'button']) }}>
    {{ $slot }}
</button>

如果您需要条件性地编译其它 HTML 元素的类,但这些元素不应接收合并的属性,您可以使用 @class 【指令】。

非类属性的合并

当合并不是 class 属性时,传递给 merge 方法的值将被视为该属性的 “默认” 值。然而,与 class 属性不同,这些属性不会与注入的属性值合并,而是会被覆盖。例如,按钮组件的实现可能如下所示:

<button {{ $attributes->merge(['type' => 'button']) }}>
    {{ $slot }}
</button>

要渲染按钮组件并指定自定义的 type,可以在使用组件时指定。如果没有指定 type,则使用 button 的默认类型:

<x-button type="submit">
    Submit
</x-button>

在这个例子中,button 组件渲染出的 HTML 将是:

<button type="submit">
    Submit
</button>

如果你希望某个属性(除了 class)将其默认值与注入的值合并在一起,可以使用 prepends 方法。在这个例子中,data-controller 属性将始终以 profile-controller 开头,任何额外注入的 data-controller 值将会被放在这个默认值之后。

<div {{ $attributes->merge(['data-controller' => $attributes->prepends('profile-controller')]) }}>
    {{ $slot }}
</div>

检索和过滤属性

您可以使用 filter 方法过滤属性。此方法接受一个闭包,闭包应该返回 true,如果您希望保留该属性在属性袋中:

{{ $attributes->filter(fn (string $value, string $key) => $key == 'foo') }}

为了方便,您可以使用 whereStartsWith 方法来检索所有键以给定字符串开头的属性:

{{ $attributes->whereStartsWith('wire:model') }}

相反,您可以使用 whereDoesntStartWith 方法来排除所有键以给定字符串开头的属性:

{{ $attributes->whereDoesntStartWith('wire:model') }}

使用 first 方法,您可以渲染属性袋中的第一个属性:

{{ $attributes->whereStartsWith('wire:model')->first() }}

如果您想检查某个属性是否存在于组件中,可以使用 has 方法。该方法接受属性名称作为唯一参数,并返回一个布尔值,指示该属性是否存在:

@if ($attributes->has('class'))
    <div>Class 属性存在</div>
@endif

如果传递一个数组给 has 方法,方法将确定组件中是否存在所有给定的属性:

@if ($attributes->has(['name', 'class']))
    <div>所有属性都存在</div>
@endif

hasAny 方法可以用于确定是否存在任何给定的属性:

@if ($attributes->hasAny(['href', ':href', 'v-bind:href']))
    <div>至少有一个属性存在</div>
@endif

您可以使用 get 方法检索特定属性的值:

{{ $attributes->get('class') }}

保留关键字

默认情况下,一些关键字被保留用于 Blade 的内部使用,以便渲染组件。以下关键字不能作为组件中的公共属性或方法名称定义:

  • data

  • render

  • resolveView

  • shouldRender

  • view

  • withAttributes

  • withName

插槽

你经常需要通过 “插槽” 将额外的内容传递给你的组件。组件插槽通过回显 $slot 变量来渲染。为了更好地理解这个概念,假设我们有一个 alert 组件,它的标记如下:

<!-- /resources/views/components/alert.blade.php -->

<div class="alert alert-danger">
    {{ $slot }}
</div>

我们可以通过将内容注入到组件中来传递内容到插槽:

<x-alert>
    <strong>Whoops!</strong> Something went wrong!
</x-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 方法来判断插槽是否包含内容:

<span class="alert-title">{{ $title }}</span>

<div class="alert alert-danger">
    @if ($slot->isEmpty())
        This is default content if the slot is empty.
    @else
        {{ $slot }}
    @endif
</div>

此外,hasActualContent 方法可以用来检查插槽是否包含任何 “实际” 内容,而不是 HTML 注释:

@if ($slot->hasActualContent())
    The scope has non-comment content.
@endif

Scoped Slots(作用域插槽)

如果你使用过像 Vue 这样的 JavaScript 框架,可能会熟悉 “作用域插槽”,它允许你在插槽中访问来自组件的数据或方法。在 Laravel 中,你也可以通过在组件上定义公共方法或属性,并通过 $component 变量在插槽中访问组件来实现类似的行为。在这个例子中,我们假设 x-alert 组件的类中定义了一个公共的 formatAlert 方法:

<x-alert>
    <x-slot:title>
        {{ $component->formatAlert('Server Error') }}
    </x-slot>

    <strong>Whoops!</strong> Something went wrong!
</x-alert>

插槽属性

像 Blade 组件一样,你也可以给插槽分配额外的【属性】,如 CSS 类名:

<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>

内联组件视图

对于非常小的组件,可能会觉得同时管理组件类和组件的视图模板有些繁琐。因此,你可以直接从 render 方法返回组件的标记:

/**
 * 获取表示组件的视图/内容。
 */
public function render(): string
{
    return <<<'blade'
        <div class="alert alert-danger">
            {{ $slot }}
        </div>
    blade;
}

生成内联视图组件

要创建一个渲染内联视图的组件,可以在执行 make:component 命令时使用 inline 选项:

php artisan make:component Alert --inline

动态组件

有时你可能需要渲染一个组件,但直到运行时才知道应该渲染哪个组件。在这种情况下,你可以使用 Laravel 内置的 dynamic-component 组件,根据运行时的值或变量来渲染组件:

// $componentName = "secondary-button";

<x-dynamic-component :component="$componentName" class="mt-4" />

手动注册组件

以下关于手动注册组件的文档主要适用于编写包含视图组件的 Laravel 包的开发者。如果你不是在编写包,那么这部分组件文档可能对你不太相关。

在为自己的应用编写组件时,组件会自动在 app/View/Components 目录和 resources/views/components 目录中进行自动发现。

但是,如果你正在构建一个包含 Blade 组件的包,或者将组件放置在非常规目录中,你需要手动注册组件类及其 HTML 标签别名,以便 Laravel 知道在哪里查找该组件。通常,你应该在包的服务提供者的 boot 方法中注册你的组件:

use Illuminate\Support\Facades\Blade;
use VendorPackage\View\Components\AlertComponent;

/**
 * 启动你的包服务。
 */
public function boot(): void
{
    Blade::component('package-alert', AlertComponent::class);
}

注册完组件后,可以通过其标签别名进行渲染:

<x-package-alert/>

自动加载包组件

另外,你可以使用 componentNamespace 方法按照约定自动加载组件类。例如,一个名为 Nightshade 的包可能包含 CalendarColorPicker 组件,这些组件位于 Package\Views\Components 命名空间下:

use Illuminate\Support\Facades\Blade;

/**
 * 启动你的包服务。
 */
public function boot(): void
{
    Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
}

这样,你可以使用包的命名空间和 package-name:: 语法来使用包组件:

<x-nightshade::calendar />
<x-nightshade::color-picker />

Blade 会自动通过 Pascal 大小写(驼峰式)检测与该组件关联的类。子目录也支持使用 "dot"(点)表示法。

匿名组件

与内联组件类似,匿名组件提供了一种通过单个文件管理组件的机制。然而,匿名组件只使用一个视图文件,并且没有关联的类。要定义一个匿名组件,你只需将一个 Blade 模板放置在 resources/views/components 目录下。例如,假设你在 resources/views/components/alert.blade.php 中定义了一个组件,你可以像这样简单地渲染它:

<x-alert/>

你可以使用 . 字符来表示组件在 components 目录下的更深层次嵌套。例如,假设该组件定义在 resources/views/components/inputs/button.blade.php,你可以像这样渲染它:

<x-inputs.button/>

匿名索引组件

有时,当一个组件由多个 Blade 模板组成时,你可能希望将该组件的模板组织在一个单独的目录中。例如,假设你有一个 “手风琴” 组件,其目录结构如下:

/resources/views/components/accordion.blade.php
/resources/views/components/accordion/item.blade.php

这个目录结构允许你像这样渲染手风琴组件及其项目:

<x-accordion>
    <x-accordion.item>
        ...
    </x-accordion.item>
</x-accordion>

然而,为了通过 x-accordion 渲染手风琴组件,我们不得不将 “index” 手风琴组件模板放在 resources/views/components 目录中,而不是将其与其它手风琴相关的模板一起嵌套在 accordion 目录内。

幸运的是,Blade 允许你在组件的目录内放置一个与组件目录名匹配的文件。这个模板存在时,即使它嵌套在一个目录中,它也可以作为该组件的 “根” 元素进行渲染。因此,我们可以继续使用上面示例中的相同 Blade 语法,但我们会将目录结构调整如下:

/resources/views/components/accordion/accordion.blade.php
/resources/views/components/accordion/item.blade.php

数据属性 / 属性

由于匿名组件没有关联的类,你可能会想知道如何区分哪些数据应该作为变量传递给组件,哪些属性应该放入组件的【属性包】中。

你可以通过在组件的 Blade 模板顶部使用 @props 指令来指定哪些属性应该被视为数据变量。组件上的所有其它属性将通过组件的属性包提供。如果你希望给数据变量指定默认值,你可以将变量名作为数组的键,默认值作为数组的值:

<!-- /resources/views/components/alert.blade.php -->

@props(['type' => 'info', 'message'])

<div {{ $attributes->merge(['class' => 'alert alert-'.$type]) }}>
    {{ $message }}
</div>

根据上面的组件定义,你可以像这样渲染组件:

<x-alert type="error" :message="$message" class="mb-4"/>

访问父数据

有时你可能希望在子组件中访问父组件的数据。在这种情况下,你可以使用 @aware 指令。例如,假设我们正在构建一个复杂的菜单组件,其中包含父组件 <x-menu> 和子组件 <x-menu.item>

<x-menu color="purple">
    <x-menu.item>...</x-menu.item>
    <x-menu.item>...</x-menu.item>
</x-menu>

<x-menu> 组件的实现可能如下所示:

<!-- /resources/views/components/menu/index.blade.php -->

@props(['color' => 'gray'])

<ul {{ $attributes->merge(['class' => 'bg-'.$color.'-200']) }}>
    {{ $slot }}
</ul>

因为 color 属性只传递给了父组件(<x-menu>),它将不会在 <x-menu.item> 中直接可用。然而,如果我们使用 @aware 指令,就可以使它在 <x-menu.item> 中也可用:

<!-- /resources/views/components/menu/item.blade.php -->

@aware(['color' => 'gray'])

<li {{ $attributes->merge(['class' => 'text-'.$color.'-800']) }}>
    {{ $slot }}
</li>

需要注意的是,@aware 指令无法访问父组件中未通过 HTML 属性显式传递的数据。未显式传递给父组件的默认 @props 值,@aware 指令是无法访问的。

匿名组件路径

如前所述,匿名组件通常通过将 Blade 模板放置在 resources/views/components 目录中来定义。然而,有时你可能希望在默认路径之外,向 Laravel 注册其它匿名组件路径。

anonymousComponentPath 方法接受两个参数,第一个参数是匿名组件的位置路径,第二个参数是可选的命名空间,用于指定组件应放置在哪个命名空间下。通常,这个方法应该在应用程序的【服务提供者】的 boot 方法中调用:

/**
 * 启动应用程序的任何服务。
 */
public function boot(): void
{
    Blade::anonymousComponentPath(__DIR__.'/../components');
}

当没有指定前缀时,注册的组件路径可以在你的 Blade 组件中直接渲染,而无需添加前缀。例如,如果在上述注册路径下存在 panel.blade.php 组件,可以像下面这样渲染:

<x-panel />

可以将前缀 "namespaces" 作为第二个参数提供给 anonymousComponentPath 方法:

Blade::anonymousComponentPath(__DIR__.'/../components', 'dashboard');

当提供前缀时,可以通过在渲染组件时将组件的命名空间前缀添加到组件名称中,来渲染该 "命名空间" 内的组件:

<x-dashboard::panel />

构建布局

使用组件构建布局

大多数 web 应用程序在各个页面之间保持相同的总体布局。如果我们在每个视图中都必须重复整个布局的 HTML,这将非常麻烦且难以维护。幸运的是,我们可以将这个布局定义为一个单独的 【Blade 组件】,并在整个应用程序中使用它。

定义布局组件

例如,假设我们正在构建一个 "todo" 列表应用程序。我们可以定义一个如下的 layout 组件:

<!-- resources/views/components/layout.blade.php -->

<html>
    <head>
        <title>{{ $title ?? 'Todo Manager' }}</title>
    </head>
    <body>
        <h1>Todos</h1>
        <hr/>
        {{ $slot }}
    </body>
</html>

应用布局组件

定义好 layout 组件后,我们可以创建一个使用该组件的 Blade 视图。在此示例中,我们将定义一个简单的视图来显示任务列表:

<!-- resources/views/tasks.blade.php -->

<x-layout>
    @foreach ($tasks as $task)
        <div>{{ $task }}</div>
    @endforeach
</x-layout>

请记住,注入到组件中的内容将被提供到我们布局组件中的默认 $slot 变量中。正如您可能已经注意到的,我们的布局还会尊重 $title 插槽(如果提供了的话);否则,将显示默认标题。我们可以使用【组件文档】中讨论的标准插槽语法,从任务列表视图中注入一个自定义标题:

<!-- resources/views/tasks.blade.php -->

<x-layout>
    <x-slot:title>
        Custom Title
    </x-slot>

    @foreach ($tasks as $task)
        <div>{{ $task }}</div>
    @endforeach
</x-layout>

现在,我们已经定义了布局和任务列表视图,我们只需要从路由返回 task 视图:

use App\Models\Task;

Route::get('/tasks', function () {
    return view('tasks', ['tasks' => Task::all()]);
});

使用模板继承构建布局

定义布局

布局也可以通过 “模板继承” 来创建。这是在引入组件之前构建应用程序的主要方式。

首先,让我们看一个简单的例子。我们将首先检查一个页面布局。由于大多数 web 应用程序在不同页面之间保持相同的总体布局,因此将这个布局定义为一个 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>

如你所见,这个文件包含了典型的 HTML 标记。然而,请注意 @section@yield 指令。@section 指令如其名所示,定义了一个内容部分,而 @yield 指令用于显示给定部分的内容。

现在我们已经为应用程序定义了一个布局,接下来让我们定义一个继承该布局的子页面。

扩展布局

在定义子视图时,使用 @extends Blade 指令来指定子视图应该 “继承” 哪个布局。扩展 Blade 布局的视图可以使用 @section 指令将内容注入到布局的各个部分中。记住,正如上面的例子所示,这些部分的内容将使用 @yield 在布局中显示:

<!-- 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

在这个例子中,sidebar 部分使用了 @parent 指令来附加(而不是覆盖)内容到布局的 sidebar 中。当视图被渲染时,@parent 指令会被布局中的内容替换。

与之前的例子不同,这个 sidebar 部分以 @endsection 结尾,而不是 @show@endsection 仅定义一个部分,而 @show 会定义并立即输出该部分。

@yield 指令也可以接受一个默认值作为第二个参数。如果被 @yield 的部分未定义,这个默认值将被渲染:

@yield('content', 'Default content')

表单

CSRF 字段

在应用程序中定义 HTML 表单时,您应当在表单中包含一个隐藏的 CSRF 令牌字段,以便 【CSRF 保护】中间件可以验证该请求。您可以使用 @csrf Blade 指令来生成令牌字段:

<form method="POST" action="/profile">
    @csrf

    ...
</form>

方法字段

由于 HTML 表单无法直接发起 PUTPATCHDELETE 请求,您需要添加一个隐藏的 _method 字段来伪造这些 HTTP 动词。@method Blade 指令可以为您生成这个字段:

<form action="/foo/bar" method="POST">
    @method('PUT')

    ...
</form>

验证错误

@error 指令可以用来快速检查某个属性是否存在【验证错误消息】。在 @error 指令内部,您可以输出 $message 变量来显示错误消息:

<!-- /resources/views/post/create.blade.php -->

<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

由于 @error 指令会编译成一个 if 语句,您可以使用 @else 指令在属性没有错误时渲染内容:

<!-- /resources/views/auth.blade.php -->

<label for="email">Email address</label>

<input
    id="email"
    type="email"
    class="@error('email') is-invalid @else is-valid @enderror"
/>

您可以通过在 @error 指令的第二个参数传递一个【特定的错误包名称】,来获取多个表单页面中的验证错误消息:

<!-- /resources/views/auth.blade.php -->

<label for="email">Email address</label>

<input
    id="email"
    type="email"
    class="@error('email', 'login') is-invalid @enderror"
/>

@error('email', 'login')
    <div class="alert alert-danger">{{ $message }}</div>
@enderror

Blade 允许您将内容推送到命名栈中,然后在另一个视图或布局中渲染这些内容。这对于指定子视图所需的任何 JavaScript 库特别有用:

@push('scripts')
    <script src="/example.js"></script>
@endpush

如果您希望在给定的布尔表达式为 true@push 内容,可以使用 @pushIf 指令:

@pushIf($shouldPush, 'scripts')
    <script src="/example.js"></script>
@endPushIf

您可以根据需要多次推送内容到栈中。要渲染完整的栈内容,只需传递栈的名称给 @stack 指令:

<head>
    <!-- Head Contents -->

    @stack('scripts')
</head>

如果您希望将内容添加到栈的开头,应使用 @prepend 指令:

@push('scripts')
    This will be second...
@endpush

// Later...

@prepend('scripts')
    This will be first...
@endprepend

服务注入

@inject 指令可用于从 Laravel 【服务容器】中检索服务。传递给 @inject 的第一个参数是服务将被放入的变量名,而第二个参数是您希望解析的服务的类名或接口名:

@inject('metrics', 'App\Services\MetricsService')

<div>
    Monthly Revenue: {{ $metrics->monthlyRevenue() }}.
</div>

渲染内联 Blade 模板

有时,您可能需要将原始的 Blade 模板字符串转换为有效的 HTML。您可以使用 Blade facade 提供的 render 方法来实现这一点。render 方法接受 Blade 模板字符串和一个可选的数据数组,以便将数据传递给模板:

use Illuminate\Support\Facades\Blade;

return Blade::render('Hello, {{ $name }}', ['name' => 'Julian Bashir']);

Laravel 通过将它们写入 storage/framework/views 目录来渲染内联的 Blade 模板。如果您希望在渲染 Blade 模板后,Laravel 删除这些临时文件,可以向该方法提供 deleteCachedView 参数:

return Blade::render(
    'Hello, {{ $name }}',
    ['name' => 'Julian Bashir'],
    deleteCachedView: true
);

渲染 Blade 片段

在使用前端框架如 Turbohtmx 时,您可能需要在 HTTP 响应中仅返回 Blade 模板的一部分。Blade “片段” 允许您实现这一点。开始时,您可以将 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 方法允许您根据给定条件有条件地返回视图的片段。否则,将返回整个视图:

return view('dashboard', ['users' => $users])
    ->fragmentIf($request->hasHeader('HX-Request'), 'user-list');

fragmentsfragmentsIf 方法允许您在响应中返回多个视图片段。片段将被连接在一起:

view('dashboard', ['users' => $users])
    ->fragments(['user-list', 'comment-list']);

view('dashboard', ['users' => $users])
    ->fragmentsIf(
        $request->hasHeader('HX-Request'),
        ['user-list', 'comment-list']
    );

扩展 Blade

Blade 允许您使用 directive 方法定义自定义指令。当 Blade 编译器遇到自定义指令时,它将调用提供的回调,并传递指令中包含的表达式。

以下示例创建了一个 @datetime($var) 指令,用于格式化给定的 $var,该变量应该是一个 DateTime 实例:

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * 注册任何应用程序服务。
     */
    public function register(): void
    {
        // ...
    }

    /**
     * 启动任何应用程序服务。
     */
    public function boot(): void
    {
        Blade::directive('datetime', function (string $expression) {
            return "<?php echo ($expression)->format('m/d/Y H:i'); ?>";
        });
    }
}

如您所见,我们将在传递给指令的表达式上链式调用 format 方法。因此,在这个示例中,指令生成的最终 PHP 代码将是:

<?php echo ($var)->format('m/d/Y H:i'); ?>

在更新 Blade 指令的逻辑后,您需要删除所有缓存的 Blade 视图。可以使用 view:clear Artisan 命令来清除缓存的 Blade 视图。

自定义 Echo 处理器

如果您尝试在 Blade 中 “回显” 一个对象,PHP 会调用该对象的 __toString 方法。__toString 是 PHP 内置的 “魔术方法” 之一。然而,有时候您无法控制给定类的 __toString 方法,例如当您与第三方库中的类进行交互时。

在这种情况下,Blade 允许您为特定类型的对象注册自定义的回显处理器。要实现这一点,您应该调用 Blade 的 stringable 方法。stringable 方法接受一个闭包,这个闭包应该类型提示它所负责渲染的对象类型。通常,stringable 方法应该在应用程序的 AppServiceProvider 类的 boot 方法中调用:

use Illuminate\Support\Facades\Blade;
use Money\Money;

/**
 * 启动任何应用程序服务。
 */
public function boot(): void
{
    Blade::stringable(function (Money $money) {
        return $money->formatTo('en_GB');
    });
}

一旦定义了自定义的回显处理器,您就可以在 Blade 模板中直接回显该对象:

Cost: {{ $money }}

自定义 If 语句

编写自定义指令有时会比定义简单的自定义条件语句更复杂。因此,Blade 提供了一个 Blade::if 方法,允许您使用闭包快速定义自定义条件指令。例如,假设我们要定义一个自定义条件,检查应用程序的默认 "磁盘" 配置。我们可以在 AppServiceProviderboot 方法中实现这一点:

use Illuminate\Support\Facades\Blade;

/**
 * 启动任何应用程序服务。
 */
public function boot(): void
{
    Blade::if('disk', function (string $value) {
        return config('filesystems.default') === $value;
    });
}

定义了自定义条件之后,您可以在模板中使用它:

@disk('local')
    <!-- 应用程序正在使用本地磁盘... -->
@elsedisk('s3')
    <!-- 应用程序正在使用 S3 磁盘... -->
@else
    <!-- 应用程序正在使用其它磁盘... -->
@enddisk

@unlessdisk('local')
    <!-- 应用程序没有使用本地磁盘... -->
@enddisk