Blade 模板
简介
Blade 是 Laravel 附带的简单而强大的模板引擎。与一些 PHP 模板引擎不同,Blade 不会限制你在模板中使用纯 PHP 代码。实际上,所有 Blade 模板都编译成纯 PHP 代码并缓存,直到它们被修改,这意味着 Blade 不会给你的应用程序带来任何额外的开销。Blade 模板文件使用 .blade.php
文件扩展名,通常存储在 resources/views
目录中。
可以使用全局 view
助手函数从路由或控制器返回 Blade 视图。当然,正如关于 视图 的文档中所述,可以使用 view
助手函数的第二个参数将数据传递给 Blade 视图。
Route::get('/', function () { return view('greeting', ['name' => 'Finn']);});
使用 Livewire 增强 Blade
想要将你的 Blade 模板提升到一个新的水平,并轻松构建动态界面?试试 Laravel Livewire。Livewire 允许你编写 Blade 组件,这些组件通过动态功能增强,而这些功能通常只有通过 React 或 Vue 这样的前端框架才能实现,为构建现代、响应式前端提供了一种很棒的方法,而无需像许多 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 实体进行双重编码。如果你想禁用双重编码,可以在 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 移除;但是,{{ name }}
表达式将保持 Blade 引擎的原样,允许它被你的 JavaScript 框架渲染。
@
符号也可以用来转义 Blade 指令:
{{-- Blade template --}}@@if() <!-- HTML output -->@if()
渲染 JSON
有时,你可能会将一个数组传递给你的视图,目的是将其作为 JSON 渲染,以初始化 JavaScript 变量。例如:
<script> var app = <?php echo json_encode($array); ?>;</script>
但是,与其手动调用 json_encode
,你可以使用 Illuminate\Support\Js::from
方法指令。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 模板是基于正则表达式,尝试将复杂的表达式传递给指令可能会导致意外的错误。
@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) 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 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
如果需要,可以使用 @auth
和 @guest
指令指定要检查的身份验证守卫:
@auth('admin') // The user is authenticated...@endauth @guest('admin') // The user is not authenticated...@endguest
环境指令
可以使用 @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
节指令
可以使用 @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
、@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
循环变量
在遍历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
如果在嵌套循环中,可以通过parent
属性访问父循环的$loop
变量。
@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
指令有条件地编译 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
指令可用于有条件地将内联 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 复选框输入是否被“选中”。如果提供的条件计算结果为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" @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'])
如果要@include
一个视图,前提是给定的布尔表达式计算结果为true
或false
,则可以使用@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
指令的第一个参数是要为数组或集合中的每个元素渲染的视图。第二个参数是要迭代的数组或集合,第三个参数是在视图中分配给当前迭代的变量名。例如,如果迭代的是一个jobs
数组,通常希望在视图中将每个作业访问为job
变量。当前迭代的数组键将在视图中作为key
变量提供。
也可以将第四个参数传递给@each
指令。此参数确定如果给定数组为空,将渲染哪个视图。
@each('view.name', $jobs, 'job', 'view.empty')
通过@each
渲染的视图不会继承来自父视图的变量。如果子视图需要这些变量,则应使用@foreach
和@include
指令。
@once
指令
@once
指令允许你定义模板的一部分,该部分在每次渲染周期内只会被评估一次。这对于使用堆栈将给定的 JavaScript 部分推送到页面的头部可能很有用。例如,如果在循环中渲染给定的组件,可能希望只在第一次渲染组件时将 JavaScript 推送到头部。
@once @push('scripts') <script> // Your custom JavaScript... </script> @endpush@endonce
由于@once
指令通常与@push
或@prepend
指令一起使用,因此为方便起见,提供了@pushOnce
和@prependOnce
指令。
@pushOnce('scripts') <script> // Your custom 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 中。
{{-- This comment will not be present in the rendered 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
上面的命令将在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; /** * Bootstrap your package's services. */public function boot(): void{ Blade::component('package-alert', Alert::class);}
注册组件后,可以使用其标签别名来渲染它。
<x-package-alert/>
或者,可以使用componentNamespace
方法通过约定自动加载组件类。例如,Nightshade
包可能在Package\Views\Components
命名空间中包含Calendar
和ColorPicker
组件。
use Illuminate\Support\Facades\Blade; /** * Bootstrap your package's services. */public function boot(): void{ Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');}
这将允许使用包组件的供应商命名空间,使用package-name::
语法。
<x-nightshade::calendar /><x-nightshade::color-picker />
Blade 会自动检测与该组件关联的类,方法是将组件名称转换为帕斯卡大小写。子目录也支持使用“点”符号。
渲染组件
要显示组件,可以在 Blade 模板中使用 Blade 组件标签。Blade 组件标签以字符串x-
开头,后跟组件类的连字符大小写名称。
<x-alert/> <x-user-profile/>
如果组件类在app/View/Components
目录中嵌套更深,可以使用.
字符来指示目录嵌套。例如,如果假设组件位于app/View/Components/Inputs/Button.php
,则可以这样渲染它。
<x-inputs.button/>
如果要条件渲染组件,可以在组件类中定义一个shouldRender
方法。如果shouldRender
方法返回false
,则不会渲染组件。
use Illuminate\Support\Str; /** * Whether the component should be rendered */public function shouldRender(): bool{ return Str::length($this->message) > 0;}
向组件传递数据
可以使用 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
指定,而kebab-case
应在 HTML 属性中引用参数名时使用。例如,给定以下组件构造函数。
/** * Create the component instance. */public function __construct( public string $alertType,) {}
$alertType
参数可以这样提供给组件。
<x-alert alert-type="danger" />
短属性语法
将属性传递给组件时,也可以使用“短属性”语法。这通常很方便,因为属性名通常与其对应的变量名匹配。
{{-- Short attribute syntax... --}}<x-profile :$userId :$name /> {{-- Is equivalent to... --}}<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` 方法返回一个闭包。
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-` 前缀后的名称。因此,`
闭包应该返回一个字符串。如果返回的字符串对应于现有的视图,则将渲染该视图;否则,将返回的字符串评估为内联 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 }}> <!-- Component content --></div>
目前不支持在组件标签中使用 `@env` 等指令。例如,`
默认/合并属性
有时您可能需要为属性指定默认值或将其他值合并到某些组件属性中。为此,您可以使用属性包的 `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` 属性的属性时,提供给 `merge` 方法的值将被视为属性的“默认”值。但是,与 `class` 属性不同,这些属性不会与注入的属性值合并。相反,它们将被覆盖。例如,`button` 组件的实现可能如下所示
<button {{ $attributes->merge(['type' => 'button']) }}> {{ $slot }}</button>
要使用自定义 `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 attribute is present</div>@endif
如果将数组传递给 `has` 方法,该方法将确定组件上是否存在所有给定的属性。
@if ($attributes->has(['name', 'class'])) <div>All of the attributes are present</div>@endif
可以使用 `hasAny` 方法确定组件上是否存在任何给定的属性。
@if ($attributes->hasAny(['href', ':href', 'v-bind:href'])) <div>One of the attributes 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` 组件以允许注入“标题”插槽。
<!-- /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
作用域插槽
如果您使用过 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` 方法返回组件的标记。
/** * Get the view / contents that represent the component. */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; /** * Bootstrap your package's services. */public function boot(): void{ Blade::component('package-alert', AlertComponent::class);}
注册组件后,可以使用其标签别名来渲染它。
<x-package-alert/>
自动加载包组件
或者,可以使用componentNamespace
方法通过约定自动加载组件类。例如,Nightshade
包可能在Package\Views\Components
命名空间中包含Calendar
和ColorPicker
组件。
use Illuminate\Support\Facades\Blade; /** * Bootstrap your package's services. */public function boot(): void{ Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');}
这将允许使用包组件的供应商命名空间,使用package-name::
语法。
<x-nightshade::calendar /><x-nightshade::color-picker />
Blade 会自动检测与该组件关联的类,方法是将组件名称转换为帕斯卡大小写。子目录也支持使用“点”符号。
匿名组件
与内联组件类似,匿名组件提供了一种通过单个文件管理组件的机制。但是,匿名组件使用单个视图文件,并且没有关联的类。要定义匿名组件,您只需要将 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` 渲染手风琴组件,我们被迫将“索引”手风琴组件模板放置在 `resources/views/components` 目录中,而不是将其嵌套在 `accordion` 目录中与其他手风琴相关的模板中。
谢天谢地,Blade 允许您在组件的模板目录中放置一个 `index.blade.php` 文件。当组件存在 `index.blade.php` 模板时,它将被渲染为组件的“根”节点。因此,我们可以继续使用上面示例中给出的相同 Blade 语法;但是,我们将调整我们的目录结构,如下所示
/resources/views/components/accordion/index.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
方法调用。
/** * Bootstrap any application services. */public function boot(): void{ Blade::anonymousComponentPath(__DIR__.'/../components');}
当组件路径在没有指定前缀的情况下注册时,就像上面的示例中一样,它们也可以在您的 Blade 组件中渲染,而不需要相应的 前缀。例如,如果在上面注册的路径中存在 panel.blade.php
组件,则可以像这样渲染它。
<x-panel />
可以将“命名空间”前缀作为 anonymousComponentPath
方法的第二个参数提供。
Blade::anonymousComponentPath(__DIR__.'/../components', 'dashboard');
当提供前缀时,可以在渲染组件时,通过将组件的命名空间前缀添加到组件名称来渲染该“命名空间”内的组件。
<x-dashboard::panel />
构建布局
使用组件的布局
大多数 Web 应用程序在各种页面中保持相同的通用布局。如果我们在我们创建的每个视图中都必须重复整个布局 HTML,那将非常麻烦且难以维护我们的应用程序。值得庆幸的是,将此布局定义为单个 Blade 组件,然后在整个应用程序中使用它,这很方便。
定义布局组件
例如,假设我们正在构建一个“待办事项”列表应用程序。我们可以定义一个如下所示的 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>
请记住,注入到组件中的内容将被提供给 layout
组件中的默认 $slot
变量。您可能已经注意到,我们的 layout
还尊重 $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
指令将内容附加(而不是覆盖)到布局的边栏。当视图渲染时,@parent
指令将被布局的内容替换。
与前面的示例相反,此 sidebar
部分以 @endsection
而不是 @show
结尾。@endsection
指令将只定义一个部分,而 @show
将定义并立即生成该部分。
@yield
指令还接受一个默认值作为其第二个参数。如果要生成的节 undefined,则将渲染此值。
@yield('content', 'Default content')
表单
CSRF 字段
在应用程序中定义 HTML 表单时,应在表单中包含一个隐藏的 CSRF 令牌字段,以便 CSRF 保护 中间件可以验证请求。您可以使用 @csrf
Blade 指令生成令牌字段。
<form method="POST" action="/profile"> @csrf ...</form>
方法字段
由于 HTML 表单无法进行 PUT
、PATCH
或 DELETE
请求,因此您需要添加一个隐藏的 _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
门面提供的 render
方法来完成此操作。render
方法接受 Blade 模板字符串以及一个可选的数据数组,以提供给模板。
use Illuminate\Support\Facades\Blade; return Blade::render('Hello, {{ $name }}', ['name' => 'Julian Bashir']);
Laravel 通过将内联 Blade 模板写入 storage/framework/views
目录来渲染它们。如果您希望 Laravel 在渲染 Blade 模板后删除这些临时文件,您可以将 deleteCachedView
参数提供给该方法。
return Blade::render( 'Hello, {{ $name }}', ['name' => 'Julian Bashir'], deleteCachedView: true);
渲染 Blade 片段
当使用 Turbo 和 htmx 等前端框架时,您可能偶尔需要仅在您的 HTTP 响应中返回 Blade 模板的一部分。Blade “片段”允许您做到这一点。首先,将 Blade 模板的一部分放在 @fragment
和 @endfragment
指令中。
@fragment('user-list') <ul> @foreach ($users as $user) <li>{{ $user->name }}</li> @endforeach </ul>@endfragment
然后,当渲染使用此模板的视图时,可以调用 fragment
方法来指定应该将指定的片段包含在传出的 HTTP 响应中。
return view('dashboard', ['users' => $users])->fragment('user-list');
fragmentIf
方法允许您根据给定的条件有条件地返回视图的片段。否则,将返回整个视图。
return view('dashboard', ['users' => $users]) ->fragmentIf($request->hasHeader('HX-Request'), 'user-list');
fragments
和 fragmentsIf
方法允许您在响应中返回多个视图片段。这些片段将被连接在一起。
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{ /** * Register any application services. */ public function register(): void { // ... } /** * Bootstrap any application services. */ 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 “回显”一个对象,将调用该对象的 __toString
方法。 __toString
方法是 PHP 的内置“魔术方法”之一。但是,有时您可能无法控制给定类的 __toString
方法,例如,当您交互的类属于第三方库时。
在这种情况下,Blade 允许您为该特定类型的对象注册自定义回显处理程序。为此,您应该调用 Blade 的 stringable
方法。stringable
方法接受一个闭包。此闭包应该对负责渲染的对象类型进行类型提示。通常,stringable
方法应该在应用程序的 AppServiceProvider
类的 boot
方法中调用。
use Illuminate\Support\Facades\Blade;use Money\Money; /** * Bootstrap any application services. */public function boot(): void{ Blade::stringable(function (Money $money) { return $money->formatTo('en_GB'); });}
定义自定义回显处理程序后,您可以在 Blade 模板中简单地回显该对象。
Cost: {{ $money }}
自定义 if 语句
当定义简单的自定义条件语句时,编程自定义指令有时比必要时更复杂。为此,Blade 提供了一个 Blade::if
方法,它允许您使用闭包快速定义自定义条件指令。例如,让我们定义一个自定义条件,它检查应用程序配置的默认“磁盘”。我们可以在 AppServiceProvider
的 boot
方法中执行此操作。
use Illuminate\Support\Facades\Blade; /** * Bootstrap any application services. */public function boot(): void{ Blade::if('disk', function (string $value) { return config('filesystems.default') === $value; });}
定义自定义条件后,您可以在模板中使用它。
@disk('local') <!-- The application is using the local disk... -->@elsedisk('s3') <!-- The application is using the s3 disk... -->@else <!-- The application is using some other disk... -->@enddisk @unlessdisk('local') <!-- The application is not using the local disk... -->@enddisk