Blade 模板引擎


简介

Blade 是由 Laravel 提供的非常简单但功能强大的模板引擎,不同于其他流行的 PHP 模板引擎,Blade 在视图中并不约束你使用 PHP 原生代码。所有的 Blade 视图最终都会被编译成原生 PHP 代码并缓存起来直到被修改,这意味着对应用的性能而言 Blade 基本上是零开销。Blade 视图文件使用 .blade.php 文件扩展并存放在 resources/views 目录下。

模板继承

定义布局

使用 Blade 的两个最大优点是模板继承和片段组合,开始之前让我们先看一个例子。首先,我们测试“主”页面布局,由于大多数 Web 应用在不同页面中使用同一个布局,可以很方便的将这个布局定义为一个单独的 Blade 页面:

<!-- 存放在 resources/views/layouts/app.blade.php -->
    
<html>
    <head>
        <title>应用名称 - @yield('title')</title>
    </head>
    <body>
        @section('sidebar')
            这里是侧边栏
        @show
    
        <div class="container">
            @yield('content')
        </div>
    </body>
</html>

正如你所看到的,该文件包含典型的 HTML 标记,不过,注意 @section@yield 指令,前者正如其名字所暗示的,定义了一个内容片段,而后者用于显示给定片段的内容。

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

继承布局

定义子页面的时候,可以使用 Blade 的 @extends 指令来指定子页面所继承的布局,继承一个 Blade 布局的视图可以使用 @section 指令注入内容到布局定义的内容片段中,记住,如上面例子所示,这些片段的内容将会显示在布局中使用 @yield 的地方:

<!-- 存放在 resources/views/child.blade.php -->
    
@extends('layouts.app')

@section('title', 'Laravel学院')
    
@section('sidebar')
    @parent
    <p>Laravel 学院致力于提供优质 Laravel 中文学习资源</p>
@endsection
    
@section('content')
    <p>这里是主体内容,完善中...</p>
@endsection

在本例中,sidebar 片段使用 @parent 指令来追加(而非覆盖)内容到继承布局的侧边栏,@parent 指令在视图渲染时将会被布局中的内容替换。

注:与之前的示例相反,sidebar 部分以 @endsection 结束而不是 @show@endsection 指令只是定义一个 section 而 @show 指令定义并立即返回这个 section。

@yield 指令还接受一个默认值作为第二个参数,这个值会在被 yield 的 section 未定义时被渲染:

@yield('content', View::make('view.name'))

Blade 视图可以通过 view 方法直接从路由中返回:

Route::get('blade', function () {
   return view('child');
});

这样在浏览器中访问 http://blog.test/blade,就可以看到页面显示如下:

基于 Jetstream 优化页面渲染效果

现在页面还很粗糙,我们可以基于 Jetstream 内置的组件对其进行优化,将 child.blade.php 模板代码调整如下:

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            Blade 模板引擎
        </h2>
    </x-slot>

    <div>
        <div class="max-w-7xl mx-auto py-10 sm:px-6 lg:px-8">
            <x-jet-action-section>
                <x-slot name="title">这里是侧边栏</x-slot>
                <x-slot name="description">学院君致力于提供优质 Laravel 中文学习资源</x-slot>
                <x-slot name="content">这里是主体内容...</x-slot>
            </x-jet-action-section>
        </div>
    </div>
</x-app-layout>

这里 child 视图继承了 Jetstream 提供的 lauyouts/app.blade.php 布局,渲染后的页面效果如下:

-w1075

是不是好看多了。

数据显示

可以通过两个花括号包裹变量来显示传递到视图的数据,比如,如果给出如下路由:

Route::get('greeting', function () {
    return view('welcome', ['name' => '学院君']);
});

那么可以通过如下方式显示 name 变量的内容:

你好, {{ $name }}。

注:Blade 的 {{}} 语句已经经过 PHP 的 htmlentities 函数处理以避免 XSS 攻击。

当然,不限制显示到视图中的变量内容,你还可以输出任何 PHP 函数的结果,实际上,可以将任何 PHP 代码放到 Blade 模板语句中:

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

显示原生数据

默认情况下,Blade 的 {{ }} 语句已经通过 PHP 的 htmlentities 函数处理以避免 XSS 攻击,如果你不想要数据被处理,比如要输出带 HTML 元素的富文本,可以使用如下语法:

Hello, {!! $name !!}.

注:输出用户提供的内容时要当心,对用户提供的内容总是要使用双花括号包裹以避免 XSS 攻击。

渲染 JSON 内容

有时候你可能会将数据以数组方式传递到视图再将其转化为 JSON 格式以便初始化某个 JavaScript 变量,例如:

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

这样显得很麻烦,有更简便的方式来实现这个功能,那就是 Blade 的 @json 指令,@json 指令接收和 json_encode 函数一样的参数:

<script>
    var app = @json($array);

    var app = @json($array, JSON_PRETTY_PRINT);
</script>

注:你只能使用 @json 指令渲染已存在的变量。Blade 模板基于正则表达式,试图传入复杂的表达式到该指令可能会导致未知错误。

HTML 实体编码

默认情况下,Blade(以及辅助函数 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.
     *
     * @return void
     */
    public function boot()
    {
        Blade::withoutDoubleEncoding();
    }
}

Blade & JavaScript 框架

由于很多 JavaScript 框架也是用花括号来表示要显示在浏览器中的表达式,如 Vue,我们可以使用 @ 符号来告诉 Blade 渲染引擎该表达式应该保持原生格式不作改动。比如:

<h1>Laravel</h1>
Hello, @{{ name }}.

在本例中,@ 符号在编译阶段会被 Blade 移除,但是,{{ name }} 表达式将会保持不变,从而可以被 JavaScript 框架正常渲染。

@ 符号还可以用于取消 Blade 指令:

{{-- Blade --}}
@@json()

<!-- HTML output -->
@json()

@verbatim指令

如果你在模板中有很大一部分篇幅显示 JavaScript 变量,那么可以将这部分 HTML 封装在 @verbatim 指令中,这样就不需要在每个 Blade 输出表达式前加上 @ 前缀:

@verbatim
    <div class="container">
        Hello, {{ name }}.
    </div>
@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

此外,Blade 还提供了 @isset@empty 指令,分别对应 PHP 的 issetempty 方法:

@isset($records)
    // $records is defined and is not null...
@endisset

@empty($records)
    // $records is "empty"...
@endempty

认证指令

@auth@guest 指令可用于快速判断当前用户是否登录:

@auth
    // 用户已登录...
@endauth

@guest
    // 用户未登录...
@endguest

如果需要的话,你也可以在使用 @auth@guest 的时候指定认证守卫

@auth('admin')
    // The user is authenticated...
@endauth

@guest('admin')
    // The user is not authenticated...
@endguest

Section 指令

你可以使用 @hasSection 指令判断某个 section 中是否有内容:

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

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

相对的,sectionMissing 可用于判断某个 section 是否不包含内容:

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

环境指令

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

@production
    // Production specific content...
@endproduction

或者,你可以使用 @env 指令判断应用是否在指定环境运行:

@env('staging')
    // The application is running in "staging"...
@endenv
    
@env(['staging', 'production'])
    // The application is running in "staging" or "production"...
@endenv

Switch 语句

switch 语句可以通过 @switch@case@break@default@enswitch 指令构建:

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

注:在循环的时候可以使用 $loop 变量获取循环信息,例如是否是循环的第一个或最后一个迭代。

使用循环的时候还可以结束循环或跳出当前迭代:

@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

$loop 变量

在循环的时候,可以在循环体中使用 $loop 变量,该变量提供了一些有用的信息,比如当前循环索引,以及当前循环是不是第一个或最后一个迭代:

@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

如果你身处嵌套循环,可以通过 $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 代码到视图中很有用,你可以使用 @php 指令在模板中执行一段原生 PHP 代码:

@php
    //
@endphp

注:尽管 Blade 提供了这个特性,如果过于频繁地使用它意味着你在视图模板中嵌入了过多的业务逻辑,需要注意。

@once 指令

@once 指令允许你定义模板的一部分每次渲染周期只执行一次,这在使用 堆栈 功能推送给定 JavaScript 片段代码到页面头部时很有用。例如,如果要在一个循环中渲染一个组件,你可能希望只在组件首次渲染时推送 JavaScript 代码到 HTML 的 header 中:

@once
    @push('scripts')
        <script>
            // Your custom JavaScript...
        </script>
    @endpush
@endonce

表单

CSRF 字段

任何时候你想要在应用中定义 HTML 表单,都需要在表单中引入隐藏的 CSRF 令牌字段,以便 CSRF 保护中间件可以通过请求验证。你可以使用 Blade 指令 @csrf 来生成令牌字段:

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

    ...
</form>

方法字段

由于 HTML 表单不能发起 PUTPATCH 以及 DELETE 请求,你需要添加隐藏的 _method 字段来伪造这些 HTTP 请求。Blade 指令 @method 可用于创建这个字段:

<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 指令的第二个参数来获取错误消息:

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

组件

组件和插槽给内容片段(section)和布局(layout)带来了方便,不过,有些人可能会发现组件和插槽的模型更容易理解。有两种方法来编写组件:基于类的组件和匿名组件。

要创建基于类的组件,需要使用 Artisan 命令 make:component,下面我们来创建一个简单的 Alert 组件来演示如何使用组件:

php artisan make:component Alert

上述命令会在 App\View\Components 目录下存放新建的 Alert 组件类:

<?php
    
namespace App\View\Components;
    
use Illuminate\View\Component;
    
class Alert extends Component
{
    /**
     * Create a new component instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }
    
    /**
     * Get the view / contents that represent the component.
     *
     * @return \Illuminate\View\View|string
     */
    public function render()
    {
        return view('components.alert');
    }
}

此外,该命令还会为组件创建视图模板,对应视图模板位于 resources/views/components 目录下:

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

<div>
    <!-- An unexamined life is not worth living. - Socrates -->
</div>

手动注册扩展包组件

为自己应用编写组件时,系统会通过遍历 app/View/Componentsresources/views/components 目录来自动发现组件。

不过,如果你正在构建一个使用 Blade 组件的扩展包,则需要手动注册组件类及其 HTML 标签别名。通常我们在扩展包服务提供者的 boot 方法中注册组件:

use Illuminate\Support\Facades\Blade;
    
/**
 * Bootstrap your package's services.
 */
public function boot()
{
    Blade::component('package-alert', AlertComponent::class);
}

注册好了之后,就可以在视图模版中通过如下标签别名来使用 Alert 组件:

<x-package-alert/>

显示组件

要显示组件,需要在某个 Blade 模板中使用 Blade 组件标签,组件标签以 x- 开头,后面跟着「烤肉串风格」的组件类名:

<x-alert/>

<x-user-profile/>

如果组件类嵌套在 App\View\Components 目录下的更深层级,可以使用 . 进行分隔,例如,假设组件位于 App\View\Components\Inputs\Button.php,则可以这样进行渲染:

<x-inputs.button/>

传递数据到组件

你可以通过 HTML 属性从组件类传递数据到 Blade 组件模板,要传递原始值使用基本的 HTML 属性即可,要传递 PHP 表达式和变量,需要使用 : 作为属性名前缀:

<!-- 引用 Alert 组件的位置,比如 resources/views/welcome.blade.php -->
<x-alert type="error" :message="$message"/>

然后需要在组件类构造函数中定义组件的必要数据,公开属性可以自动在对应组件视图中生效,而不需要从组件类通过 render 方法传递到视图:

<?php
    
namespace App\View\Components;
    
use Illuminate\View\Component;
    
class Alert extends Component
{
    /**
     * The alert type.
     *
     * @var string
     */
    public $type;
    
    /**
     * The alert message.
     *
     * @var string
     */
    public $message;
    
    /**
     * Create the component instance.
     *
     * @param  string  $type
     * @param  string  $message
     * @return void
     */
    public function __construct($type, $message)
    {
        $this->type = $type;
        $this->message = $message;
    }
    
    /**
     * Get the view / contents that represent the component.
     *
     * @return \Illuminate\View\View|string
     */
    public function render()
    {
        return view('components.alert');
    }
}

然后在渲染组的视图模板中,你可以通过输出变量名显示组件公开属性的内容:

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

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

命名风格

组件类构造函数参数需要使用小驼峰命名风格(camelCase),而在 HTML 属性中引用该参数时则需要使用短划线命名风格(kebab-case)。示例如下:

/**
 * Create the component instance.
 *
 * @param  string  $alertType
 * @param  string  $message
 * @return void
 */
public function __construct($alertType)
{
    $this->alertType = $alertType;
}

$alertType 参数在 HTML 属性中引用方式如下:

<!-- 引用 Alert 组件的位置,比如 resources/views/welcome.blade.php -->
<x-alert alert-type="danger" />

组件方法

除了公开属性可以直接在组件模板中生效之外,任何组件类中的公开方法也可以直接在组件模板中执行,例如,假设有一个包含 isSelected 方法的组件:

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

你可以在组件模板中通过调用与方法名匹配的变量来执行这个方法:

<!-- 定义组件模板的位置,resources/views/components/alert.blade.php -->

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

使用类里面的属性 & 插槽

Blade 组件还允许你访问组件名、属性以及类渲染方法中的插槽。不过,为了访问这些数据,你需要从组件 render 方法中返回一个闭包,该闭包接收 $data 数组作为唯一参数:

/**
 * Get the view / contents that represent the component.
 *
 * @return \Illuminate\View\View|\Closure|string
 */
public function render()
{
    return function (array $data) {
        // $data['componentName'];
        // $data['attributes'];
        // $data['slot'];

        return '<div>Component content</div>';
    };
}

componentName 等于 HTML 标签 x- 前缀之后的组件名,因此 <x-alert/> 对应的 componentName 值是 alertattributes 元素会包含所有在组件 HTML 标签中出现的属性。slot 元素是一个 Illuminate\Support\HtmlString 实例,其中包含了来自组件的插槽内容。

额外依赖

如果组件需要使用 Laravel 服务容器中的依赖,可以在组件类构造函数参数列表中将它们罗列在所有组件数据属性之前,这样一来,它们就可以通过容器自动注入:

use App\AlertCreator
    
/**
 * Create the component instance.
 *
 * @param  \App\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;
}

管理属性

我们已经介绍了如何传递数据属性到组件,不过,有时候,你可能需要指定额外的 HTML 属性,例如 class,这并不是组件正常运行所需数据的一部分。通常,你可能想要将这些额外属性向下传递到组件模板的根元素,例如,假设我们想要这样渲染一个 alert 组件:

<!-- 引用 Alert 组件的位置,比如 resources/views/welcome.blade.php -->
<x-alert type="error" :message="$message" class="mt-4"/>

所有不是组件构造函数一部分的属性将会自动添加到组件的「属性包」里面,这个属性包会通过 $attributes 变量自动在组件模板中生效,所有这些属性都可以在组件模板中通过打印该变量生效:

<!-- 定义组件模板的位置,resources/views/components/alert.blade.php -->
<div {{ $attributes }}>
    <div class="alert alert-{{ $type }}" >
        {{ $message }}
    </div>
</div>

对应的渲染结果如下:

-w660

注:目前还不支持在组件上直接使用类似 @env 这样的指令。

默认/合并属性

有时候你可能需要指定属性的默认值或者合并额外值到某些组件属性中,这可以通过使用属性包的 merge 方法实现,比如我们将上面例子中的父子元素 class 属性合并:

<!-- resources/views/components/alert.blade.php -->
<div {{ $attributes->merge(['class' => 'alert alert-'.$type]) }}>
    {{ $message }}
</div>

然后我们在引用这个组件的地方调整属性定义如下:

<!-- Alert 组件,resources/views/welcome.blade.php -->
<x-alert type="error" :message="$message" class="mb-4"/>

最后,该组件的渲染结果 HTML 会变成这个样子:

-w611

过滤属性

你可以使用 filter 方法对属性进行过滤,该方法接收一个闭包,如果想要保留属性包中的对应属性值,在闭包函数中返回 true 即可:

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

为了方便,你可以使用 whereStartsWith 方法来获取所有键值以指定字符串开头的属性:

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

使用 first 方法,你可以渲染给定属性包中的第一个属性:

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

插槽

有时候,你需要通过「插槽」传递额外内容到组件,我们假设创建了这样的一个 Alert2 组件:

php artisan make:component Alert2

修改组件模板代码如下:

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

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

我们可以通过注入内容到组件标签来传递内容到 slot 插槽:

<!--引用 Alert2 组件,resources/views/welcome.blade.php-->
<x-alert2>
    <strong>Whoops!</strong> Something went wrong!
</x-alert2>

有时候一个组件需要在组件内不同位置渲染不同的插槽。下面我们来编辑 alert2 组件模板以便可以注入「标题」:

<!-- /resources/views/components/alert2.blade.php -->
    
<span class="alert-title">{{ $title }}</span>

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

你可以使用 x-slot 标签定义命名插槽的内容(比如,这里定义了一个名称为 title 的命名插槽,对应内容会注入到上面组件模板的 title 插槽中),任何不在 x-slot 标签中的内容都会被传递给组件模板中的 $slot 变量:

<!--引用 Alert2 组件,resources/views/welcome.blade.php-->
<x-alert2>
    <x-slot name="title">
        Server Error
    </x-slot>
    
    <strong>Whoops!</strong> Something went wrong!
</x-alert2>

上述组件模板渲染结果如下:

-w779

插槽作用域

如果你使用过类似 Vue 这样的 JavaScript 框架,你可能会对插槽作用域很熟悉,通过插槽作用域,你可以在组件中访问来自插槽的数据或方法。在 Laravel 中,你也可以在组件类定义公共属性或方法来实现类似的功能,只需要在插槽中通过 $component 变量引用对应的属性或方法即可:

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

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

内联组件视图

对于非常小的组件,管理组件类和组件视图模板有点麻烦,因此,你可以直接从 render 方法中返回组件 HTML 标记:

/**
 * Get the view / contents that represent the component.
 *
 * @return \Illuminate\View\View|string
 */
public function render()
{
    return <<<'blade'
        <div class="alert alert-danger">
            {{ $slot }}
        </div>
    blade;
}

生成内联视图组件

要创建一个渲染内联视图的组件,可以在执行 make:component 命令时带上 inline 选项,这样就不会生成对应的 HTML 组件模板了:

php artisan make:component Alert --inline

匿名组件

和内联组件类似,匿名组件提供了通过单文件来管理组件的机制。不过,匿名组件使用的是单视图文件,并没有与之关联的类。要定义匿名组件,你只需要在 resources/views/components 目录下存放 Blade 模板即可。例如,假设在 resources/views/components/alert3.blade.php 中定义了一个组件模板,则可以通过如下方式在其他视图模板中引用:

<x-alert3/>

同样可以使用 . 符号来标识嵌套组件,只不过此时的嵌套组件是通过视图模板目录层级进行嵌套,例如,假设该组件定义在 resources/views/components/inputs/button.blade.php 中,则可以这样进行渲染:

<x-inputs.button/>

数据属性/HTML 属性

由于匿名组件没有关联类,你可能会好奇如何将不同的数据以变量方式传递到对应的组件以及哪些属性会被存放到组件的属性包

你可以在组件 Blade 模板的顶部使用 @props 指令来指定哪些属性是数据变量,这样一来,组件上的所有其他属性都会通过组件的属性包生效:

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

@props(['type', 'message'])

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

我们可以在 welcome.blade.php 视图模板中这样引用 alert3 匿名组件:

<!-- Alert3组件(匿名组件)-->
<x-alert3 type="warning" :message="$message" class="mt-5"/>

对应的渲染结果如下:

-w630

动态组件

有时候,你可能需要渲染组件但是直到运行时都不知道要渲染哪个组件,在这种场景下,可以使用 Laravel 内置的 dynamic-component 组件基于运行时的值或变量渲染组件:

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

包含子视图

Blade 的 @include 指令允许你很轻松地在一个视图中包含另一个 Blade 视图,所有父级视图中变量在被包含的子视图中依然有效:

<div>
    @include('shared.errors')

    <form>
        <!-- Form Contents -->
    </form>
</div>

上述指令会在当前目录下的 shared 子目录中寻找 errors.blade.php 文件并将其内容引入当前视图。

尽管被包含的视图可以继承所有父视图中的数据,你还可以传递额外参数到被包含的视图:

@include('view.name', ['some' => 'data'])

当然,如果你尝试包含一个不存在的视图,Laravel 会抛出错误,如果你想要包含一个有可能不存在的视图,可以使用 @includeIf 指令:

@includeIf('view.name', ['some' => 'data'])

如果包含的视图取决于一个给定的布尔条件,可以使用 @includeWhen 指令:

@includeWhen($boolean, 'view.name', ['some' => 'data'])

@includeWhen 相对的指令是 @includeUnless,它会在给定布尔表达式值为 false 时引入视图:

@includeUnless($boolean, 'view.name', ['some' => 'data'])

要包含给定数组中的第一个视图,可以使用 @includeFirst 指令:

@includeFirst(['custom.admin', 'admin'], ['some' => 'data'])

注:不要在 Blade 视图中使用 __DIR____FILE__ 常量,因为它们会指向缓存视图的路径。

为子视图引入设置别名

如果引入的子视图存放在某个子目录中,你可能希望为它们设置别名以便于访问。例如,假设 Blade 子视图存放在 resources/views/includes/input.blade.php 中,对应内容如下:

<input type="{{ $type ?? 'text' }}">

你可以使用 include 方法来为这个引入设置别名,将 includes.input 设置为 input,通常,该操作在 AppServiceProviderboot 方法中完成:

use Illuminate\Support\Facades\Blade;

Blade::include('includes.input', 'input');

这样一来,就可以将设置的别名作为 Blade 指令用来渲染子视图了:

@input(['type' => 'email'])

在视图中渲染集合数据

你可以使用 Blade 的 @each 指令通过一行代码循环引入多个局部视图:

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

该指令的第一个参数是数组或集合中每个元素要渲染的局部视图,第二个参数是你希望迭代的数组或集合,第三个参数是要分配给当前视图的变量名。举个例子,如果你要迭代一个 jobs 数组,通常你需要在局部视图中访问 $job 变量。在局部视图中可以通过 key 变量访问当前迭代的键。

你还可以传递第四个参数到 @each 指令,该参数用于指定给定数组为空时渲染的视图:

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

注:通过 @each 渲染的视图不会从父视图中继承变量,如果子视图需要这个变量,可以使用 @foreach@include 指令来替代。

堆栈

Blade 允许你推送内容到命名堆栈,以便在其他视图或布局中渲染。这在子视图中引入指定 JavaScript 库时很有用:

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

推送次数不限,要渲染完整的堆栈内容,传递堆栈名称到 @stack 指令即可:

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

    @stack('scripts')
</head>

如果你想要将内容显示在堆栈开头,需要使用 @prepend 指令:

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

// Later...

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

服务注入

@inject 指令可以用于从服务容器中获取服务,传递给 @inject 的第一个参数是服务对应的变量名,第二个参数是要解析的服务类名或接口名:

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

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

扩展 Blade

Blade 甚至还允许你自定义指令,可以使用 directive 方法来注册一个指令。当 Blade 编译器遇到该指令,将会传入参数并调用提供的回调。

下面的例子创建了一个 @datetime($var) 指令格式化给定的 DateTime 的实例 $var

<?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'); ?>";
        });
    }
    
    /**
     * 在容器中注册绑定.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

正如你所看到的,我们可以将 format 方法应用到任何传入指令的表达式上,所以,在本例中,该指令最终生成的 PHP 代码如下:

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

注:更新完 Blade 指令逻辑后,必须删除所有的 Blade 缓存视图。缓存的 Blade 视图可以通过 Artisan 命令 view:clear 移除。

自定义 If 语句

在定义一些简单、自定义的条件语句时,编写自定义指令往往复杂性大于必要性,因为这个原因,Blade 提供了一个 Blade::if 方法通过闭包的方式快速定义自定义的条件指令,例如,我们来自定义一个条件来检查当前应用的云服务提供商,可以在 AppServiceProviderboot 方法中定义这段逻辑:

use Illuminate\Support\Facades\Blade;
    
/**
 * Perform post-registration booting of services.
 *
 * @return void
 */
public function boot()
{
    Blade::if('cloud', function ($provider) {
        return config('filesystems.default') === $provider;
    });
}

定义好自定义条件后,就可以在模板中使用了:

@cloud('digitalocean')
    // The application is using the digitalocean cloud provider...
@elsecloud('aws')
    // The application is using the aws provider...
@else
    // The application is not using the digitalocean or aws environment...
@endcloud
    
@unlesscloud('aws')
    // The application is not using the aws environment...
@endcloud

Vote Vote Cancel Collect Collect Cancel

<< 上一篇: 日志

>> 下一篇: 本地化