Blade 模板
简介
Blade 是 Laravel 提供的一个简单而又强大的模板引擎。 和其他流行的 PHP 模板引擎不同,Blade 并不限制你在视图中使用原生 PHP 代码。实际上,所有 Blade 视图文件都将被编译成原生的 PHP 代码并缓存起来,除非它被修改,否则不会重新编译,这就意味着 Blade 基本上不会给你的应用增加任何负担。Blade 模板文件使用 .blade.php
作为文件扩展名,被存放在 resources/views
目录。
Blade 视图可以使用全局 view
函数从 Route 或控制器返回。当然,正如有关 views 的文档中所描述的,可以使用 view
函数的第二个参数将数据传递到 Blade 视图:
Route::get('/', function () {
return view('greeting', ['name' => 'Finn']);
});
用 Livewire 为 Blade 赋能
想让你的 Blade 模板更上一层楼,轻松构建动态界面吗?看看Laravel Livewire。Livewire 允许你编写 Blade 组件,这些组件具有动态功能,通常只能通过 React 或 Vue 等前端框架来实现,这提供了一个很好的方法来构建现代,没有复杂前端映射,基于客户端渲染,无须很多的构建步骤的 JavaScript 框架。
显示数据
你可以把变量置于花括号中以在视图中显示数据。例如,给定下方的路 由:
Route::get('/', function () {
return view('welcome', ['name' => 'Samantha']);
});
你可以像如下这样显示 name
变量的内容:
Hello, {{ $name }}.
:Blade 的 {{ }}
语句将被 PHP 的 htmlspecialchars
函数自动转义以防范 XSS 攻击。
你不仅限于显示传递给视图的变量的内容。你也可以回显任何 PHP 函数的结果。实际上,你可以将所需的任何 PHP 代码放入 Blade echo 语句中:
The current UNIX timestamp is {{ time() }}.
HTML 实体编码
默认情况下,Blade(和 Laravel e
助手)将对 HTML 实体进行双重编码。如果你想禁用双重编码,请从 AppServiceProvider
的 boot
方法调用 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 移除;当然,Blade 将不会修改 {{ name }}
表达式,取而代之的是 JavaScript 模板来对其进行渲染。
@
符号也用于转义 Blade 指令:
{{-- Blade template --}}
@@if()
<!-- HTML output -->
@if()
渲染 JSON
有时,你可能会将数组传递给视图,以将其呈现为 JSON,以便初始化 JavaScript 变量。 例如:
<script>
var app = <?php echo json_encode($array); ?>;
</script>
或者,你可以使用 Illuminate\Support\Js::from
方法指令,而不是手动调用 json_encode
。 from
方法接受与 PHP 的 json_encode
函数相同的参数;但是,它将确保正确转义生成的 JSON 以包含在 HTML 引号中。 from
方法将返回一个字符串 JSON.parse
JavaScript 语句,它将给定对象或数组转换为有效的 JavaScript 对象:
<script>
var app = {{ Illuminate\Support\Js::from($array) }};
</script>
Laravel 框架的最新版本包括一个 Js
门面,它提供了在 Blade 模板中方便地访问此功能:
<script>
var app = {{ Js::from($array) }};
</script>
你应该只使用 Js::from
渲染已经存在的变量为 JSON。 Blade 模板基于正则表达式,如果尝试将复杂表达式传递给 Js::from
可能会导致无法预测的错误。
@verbatim
指令
如果你在模板中显示很大一部分 JavaScript 变量,你可以将 HTML 嵌入到 @verbatim
指令中,这样,你就不需要在每一个 Blade 回显语句前添加 @
符号:
@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
译注:相当于
@if (! Auth::check()) @endif
除了上面所说条件指令外, @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」或 [生产] 环境...
@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
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)
<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
循环时,你可以使用 循环变量 去获取有关循环的有价值的信息,例如,你处于循环的第一个迭代亦或是处于最后一个迭代。
使用循环时,还可以使用 @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
Loop 变量
在遍历 foreach
循环时,循环内部可以使用 $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 the 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 | 嵌套循环中的父级循环。 |
有条件地编译 class 样式
该 @class
指令有条件地编译 CSS class 样式。该指令接 收一个数组,其中数组的键包含你希望添加的一个或多个样式的类名,而值是一个布尔表达式。如果数组元素有一个数值的键,它将始终包含在呈现的 class 列表中:
@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
指令可用于有条件地将内联 CSS 样式添加到一个 HTML 元素中。
@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 复选框输入是否被「选中(checked)」。如果提供的条件判断为 true
,则此指令将回显 checked
:
<input type="checkbox"
name="active"
value="active"
@checked(old('active', $user->active)) />
同样,该 @selected
指令可用于判断给定的选项是否被「选中(selected)」:
<select name="version">
@foreach ($product->versions as $version)
<option value="{{ $version }}" @selected(old('version') == $version)>
{{ $version }}
</option>
@endforeach
</select>
此外,该 @disabled
指令可用于判断给定元素是否为「禁用(disabled)」:
<button type="submit" @disabled($errors->isNotEmpty())>Submit</button>
此外,@readonly
指令可以用来指示某个元素是否应该是「只读 (readonly)」的 。
<input type="email"
name="email"
value="email@laravel.com"
@readonly($user->isNotAdmin()) />
此外,@required
指令可以用来指示一个给定的元素是否应该是「必需的(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'])
如果想要使用 @include
包含一个给定值为 true
或 false
的布尔表达式的视图,那么你可以使用 @includeWhen
和 @includeUnless
指令:
@includeWhen($boolean, 'view.name', ['status' => 'complete'])
@includeUnless($boolean, 'view.name', ['status' => 'complete'])
如果想要包含一个视图数组中第一个存在的视图,你可以使用 includeFirst
指令:
@includeFirst(['custom.admin', 'admin'], ['status' => 'complete'])
在视图中,你应该避免使用 __DIR__
和 __FILE__
这些常量,因为他们将引用已缓存的和已编译的视图。
为集合渲染视图
你可以使用 Blade 的 @each
指令将循环合并在一行内:
@each('view.name', $jobs, 'job')
该 @each
指令的第一个参数是数组或集合中的元素的要渲染的视图片段。第二个参数是你想要迭代的数组或集合,当第三个参数是一个表示当前迭代的视图的变量名。因此,如果你遍历一个名为 jobs
的数组,通常会在视图片段中使用 job
变量来访问每一个 job (jobs 数组的元素)。在你的视图片段中,可以使用 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
指令一起使用,为了使用方便,我们提供了 @pushOnce
和 @prependOnce
指令:
@pushOnce('scripts')
<script>
// 你自定义的 JavaScript 代码...
</script>
@endPushOnce
原始 PHP 语法
在许多情况下,嵌入 PHP 代码到你的视图中是很有用的。你可以在模板中使用 Blade 的 @php
指令执行原生的 PHP 代码块:
@php
$counter = 1;
@endphp
如果只需要写一条 PHP 语句,可以在 @php
指令中包含该语句。
@php($counter = 1)
注释
Blade 也允许你在视图中定义注释。但是,和 HTML 注释不同, Blade 注释不会被包含在应用返回的 HTML 中:
{{-- 这个注释将不会出现在渲染的HTML中。 --}}
组件
组件和插槽的作用与区块和布局的作用一致;不过,有些人可能觉着组件和插槽更易于理解。有两种书写组件的方法:基于类的组件和匿名组件。
你可以使用 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
上面的命令将在目录中创建一个 Input
组件, App\View\Components\Forms
视图将放置在 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 />
作为组件呈现。
手动注册包组件
当为你自己的应用编写组件的时候,Laravel 将会自动发现位于 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
包可能有 Calendar
和 ColorPicker
组件驻留在 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 将自动检测链接到该组件的类,通过对组件名称进行帕斯卡大小写。使用「点」表示法也支持子目录。
显示组件
要显示一个组件,你可以在 Blade 模板中使用 Blade 组件标签。 Blade 组件以 x-
字符串开始,其后紧接组件类 kebab case 形式的名称(即单词与单词之间使用短横线 -
进行连接):
<x-alert/>
<x-user-profile/>
如果组件位于 App\View\Components
目录的子目录中,你可以使用 .
字符来指定目录层级。例如,假设我们有一个组件位于 App\View\Components\Inputs\Button.php
,那么我们可以像这样渲染它:
<x-inputs.button/>
如果你想有条件地渲染你的组件,你可以在你的组件类上定义 一个 shouldRender
方法。如果 shouldRender
方法返回 false
,该组件将不会被渲染。
use Illuminate\Support\Str;
/**
* 该组件是否应该被渲染
*/
public function shouldRender(): bool
{
return Str::length($this->message) > 0;
}
传递数据到组件中
你可以使用 HTML 属性传递数据到 Blade 组件中。普通的值可以通过简单的 HTML 属性来传递给组件。PHP 表达式和变量应该通过以 :
字符作为前缀的变量来进行传递:
<x-alert type="error" :message="$message"/>
你应该在类的构造器中定义组件的必要数据。在组件的视图中,组件的所有 public 类型的属性都是可用的。不必通过组件类的 render
方法传递:
<?php
namespace App\View\Components;
use Illuminate\View\Component;
use Illuminate\View\View;
class Alert extends Component
{
/**
* 创建组件实例。
*/
public function __construct(
public string $type,
public string $message,
) {}
/**
* 获取代表该组件的视图/内容
*/
public function render(): View
{
return view('components.alert');
}
}
渲染组件时,你可以回显变量名来显示组件的 public 变量的内容:
<div class="alert alert-{{ $type }}">
{{ $message }}
</div>
命名方式(Casing)
组件的构造器的参数应该使用 驼峰式
类型,在 HTML 属性中引用参数名时应该使用 短横线隔开式 kebab-case :单词与单词之间使用短横线 - 进行连接)
。例如,给定如下的组件构造器:
/**
* 创建一个组件实例
*/
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
方法:
/**
* 确定给定选项是否为当前选定的选项。
*/
public function isSelected(string $option): bool
{
return $option === $this->selected;
}
你可以通过调用与方法名称匹配的变量,从组件模板执行此方法:
<option {{ $isSelected($value) ? 'selected' : '' }} value="{{ $value }}">
{{ $label }}
</option>
访问组件类中的属性和插槽
Blade 组件还允许你访问类的 render 方法中的组件名称、属性和插槽。但是,为了访问这些数据,应该从组件的 render
方法返回闭包。闭包将接收一个 $data
数组作为它的唯一参数。此数组将包含几个元素,这些元素提供有关组件的信息:
use Closure;
/**
* 获取表示组件的视图 / 内容
*/
public function render(): Closure
{
return function (array $data) {
// $data['componentName'];
// $data['attributes'];
// $data['slot'];
return '<div>Components content</div>';
};
}
componentName
等于 x-
前缀后面的 HTML 标记中使用的名称。所以 <x-alert />
的 componentName
将是 alert
。 attributes
元素将包含 HTML 标记上的所有属性。 slot
元素是一个 Illuminate\Support\HtmlString
实例,包含组件的插槽内容。
闭包应该返回一个字符串。如果返回的字符串与现有视图相对应,则将呈现该视图;否则,返回的字符串将作为内联 Blade 视图进行计算。
附加依赖项
如果你的组件需要引入来自 Laravel 的 服务容器的依赖项,你可以在组件的任何数据属性之前列出这些依赖项,这些依赖项将由容器自动注入:
use App\Services\AlertCreator;
/**
* 创建组件实例
*/
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
{
/**
* 不应向组件模板公开的属性/方法。
*
* @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">
<!-- Contents of the $message variable -->
</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 属性的合并
当合并非 class
属性的属性时,提供给 merge
方法的值将被视为该属性的「default」值。但是,与 class
属性不同,这些属性不会与注入的属性值合并。相反,它们将被覆盖。例如, button
组件的实现可能如下所示:
<button {{ $attributes->merge(['type' => 'button']) }}>
{{ $slot }}
</button>
若要使用自定义 type
呈现按钮组件,可以在使用该组件时指定它。如果未指定 type
,则将使用 button
作为 type 值:
<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 attribute is present</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>
我们可以通过向组件中注入内容将内容传递到 slot
:
<x-alert>
<strong>Whoops!</strong> Something went wrong!
</x-alert>
有时候一个组件可能需要在它内部的不同位置放置多个不同的插槽。我们来修改一下 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>
作用域插槽
如果你使用诸如 Vue 这样的 JavaScript 框架,那么你应该很熟悉「作用域插槽」,它允许你从插槽中的组件访问数据或者方法。 Laravel 中也有类似的用法,只需在你的组件中定义 public 方法或属性,并且使用 $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
组件, 根据运行时的值或变量来渲染组件:
<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
包可能有Calendar
和ColorPicker
组件,它们位于PackageViews\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-casing) 自动检测与该组件链接的类。也支持使用 "点 "符号的子目录。