模板继承与组件 #
一、模板继承 #
1.1 概述 #
模板继承允许你定义一个基础布局,然后在子模板中扩展它。
text
模板继承结构
├── 布局模板(父模板)
│ ├── @yield 定义占位区
│ └── @section 定义区块
└── 子模板
├── @extends 继承布局
└── @section 填充区块
1.2 定义布局 #
blade
<!-- resources/views/layouts/app.blade.php -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@yield('title', 'Laravel')</title>
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
@stack('styles')
</head>
<body>
@include('partials.header')
<main class="container">
@yield('content')
</main>
@include('partials.footer')
<script src="{{ asset('js/app.js') }}"></script>
@stack('scripts')
</body>
</html>
1.3 继承布局 #
blade
<!-- resources/views/users/index.blade.php -->
@extends('layouts.app')
@section('title', '用户列表')
@section('content')
<h1>用户列表</h1>
<table>
@foreach($users as $user)
<tr>
<td>{{ $user->name }}</td>
</tr>
@endforeach
</table>
@endsection
二、@yield与@section #
2.1 @yield #
@yield 定义一个占位区,用于显示内容:
blade
<!-- 布局中 -->
<title>@yield('title')</title>
<!-- 带默认值 -->
<title>@yield('title', '默认标题')</title>
2.2 @section #
@section 有两种用法:
1. 在布局中定义区块
blade
<!-- 布局中 -->
@section('sidebar')
<div class="sidebar">
默认侧边栏内容
</div>
@show
2. 在子模板中填充区块
blade
<!-- 子模板中 -->
@section('sidebar')
@parent
<div class="sidebar-extra">
额外内容
</div>
@endsection
2.3 @parent指令 #
@parent 用于在子模板中保留父模板的内容:
blade
<!-- 布局中 -->
@section('scripts')
<script src="/base.js"></script>
@show
<!-- 子模板中 -->
@section('scripts')
@parent
<script src="/page.js"></script>
@endsection
<!-- 最终输出 -->
<script src="/base.js"></script>
<script src="/page.js"></script>
2.4 覆盖区块 #
不使用 @parent 会完全覆盖父模板内容:
blade
<!-- 布局中 -->
@section('sidebar')
<p>默认侧边栏</p>
@show
<!-- 子模板中 -->
@section('sidebar')
<p>完全替换</p>
@endsection
<!-- 最终输出 -->
<p>完全替换</p>
三、多级继承 #
3.1 基础布局 #
blade
<!-- resources/views/layouts/base.blade.php -->
<!DOCTYPE html>
<html>
<head>
<title>@yield('title')</title>
</head>
<body>
@yield('content')
</body>
</html>
3.2 中间布局 #
blade
<!-- resources/views/layouts/app.blade.php -->
@extends('layouts.base')
@section('content')
<div class="app-container">
@yield('app-content')
</div>
@endsection
3.3 最终页面 #
blade
<!-- resources/views/home.blade.php -->
@extends('layouts.app')
@section('title', '首页')
@section('app-content')
<h1>欢迎</h1>
@endsection
四、Blade组件 #
4.1 创建组件 #
bash
php artisan make:component Alert
生成的文件:
text
app/View/Components/Alert.php
resources/views/components/alert.blade.php
4.2 组件类 #
php
// app/View/Components/Alert.php
namespace App\View\Components;
use Illuminate\View\Component;
class Alert extends Component
{
public $type;
public $message;
public function __construct($type = 'info', $message = '')
{
$this->type = $type;
$this->message = $message;
}
public function render()
{
return view('components.alert');
}
}
4.3 组件视图 #
blade
<!-- resources/views/components/alert.blade.php -->
<div {{ $attributes->merge(['class' => 'alert alert-' . $type]) }}>
{{ $message }}
{{ $slot }}
</div>
4.4 使用组件 #
blade
<x-alert type="success" message="操作成功!" />
<x-alert type="danger">
<strong>错误!</strong> 操作失败。
</x-alert>
<x-alert type="warning" class="mb-4">
警告信息
</x-alert>
五、组件属性 #
5.1 组件数据 #
php
// 组件类
class UserCard extends Component
{
public $user;
public $showEmail;
public function __construct($user, $showEmail = false)
{
$this->user = $user;
$this->showEmail = $showEmail;
}
public function render()
{
return view('components.user-card');
}
}
blade
<!-- 组件视图 -->
<div class="user-card">
<h3>{{ $user->name }}</h3>
@if($showEmail)
<p>{{ $user->email }}</p>
@endif
</div>
<!-- 使用 -->
<x-user-card :user="$user" :show-email="true" />
5.2 属性转kebab-case #
blade
<!-- PHP属性名: showEmail -->
<!-- HTML属性名: show-email -->
<x-user-card :show-email="true" />
5.3 $attributes变量 #
blade
<!-- 组件视图 -->
<div {{ $attributes }}>
内容
</div>
<!-- 使用 -->
<x-alert class="mb-4" id="my-alert">
消息
</x-alert>
<!-- 输出 -->
<div class="mb-4" id="my-alert">
消息
</div>
5.4 合并属性 #
blade
<div {{ $attributes->merge(['class' => 'alert']) }}>
内容
</div>
<!-- 使用 -->
<x-alert class="mb-4" />
<!-- 输出 -->
<div class="alert mb-4">
内容
</div>
5.5 属性过滤 #
blade
<!-- 只保留特定属性 -->
<button {{ $attributes->only(['class', 'disabled']) }}>
按钮
</button>
<!-- 排除特定属性 -->
<div {{ $attributes->except(['class']) }}>
内容
</div>
六、插槽 #
6.1 默认插槽 #
blade
<!-- 组件视图 -->
<div class="card">
{{ $slot }}
</div>
<!-- 使用 -->
<x-card>
卡片内容
</x-card>
6.2 具名插槽 #
blade
<!-- 组件视图 -->
<div class="card">
<div class="card-header">
{{ $title }}
</div>
<div class="card-body">
{{ $slot }}
</div>
<div class="card-footer">
{{ $footer }}
</div>
</div>
<!-- 使用 -->
<x-card>
<x-slot:title>
卡片标题
</x-slot:title>
卡片内容
<x-slot:footer>
卡片底部
</x-slot:footer>
</x-card>
6.3 插槽属性 #
blade
<!-- 组件视图 -->
<div class="alert alert-{{ $type }}">
{{ $slot }}
</div>
@props(['type' => 'info'])
<!-- 使用 -->
<x-alert type="success">
操作成功
</x-alert>
6.4 插槽变量 #
blade
<!-- 组件视图 -->
@foreach($items as $item)
{{ $slot($item) }}
@endforeach
<!-- 使用 -->
<x-list :items="$users" as="user">
<li>{{ $user->name }}</li>
</x-list>
七、匿名组件 #
7.1 创建匿名组件 #
匿名组件没有类文件,只有视图文件:
blade
<!-- resources/views/components/button.blade.php -->
@props(['type' => 'button', 'variant' => 'primary'])
<button type="{{ $type }}" {{ $attributes->merge(['class' => 'btn btn-' . $variant]) }}>
{{ $slot }}
</button>
7.2 使用匿名组件 #
blade
<x-button>默认按钮</x-button>
<x-button variant="danger">删除</x-button>
<x-button type="submit" variant="success">提交</x-button>
7.3 @props指令 #
blade
@props([
'type' => 'info',
'dismissible' => false,
])
<div {{ $attributes->merge(['class' => 'alert alert-' . $type]) }}>
{{ $slot }}
@if($dismissible)
<button type="button" class="close">×</button>
@endif
</div>
八、组件方法 #
8.1 在组件中定义方法 #
php
class Alert extends Component
{
public $type;
public function __construct($type = 'info')
{
$this->type = $type;
}
public function classes()
{
return 'alert alert-' . $this->type;
}
public function render()
{
return view('components.alert');
}
}
blade
<!-- 组件视图 -->
<div class="{{ $this->classes() }}">
{{ $slot }}
</div>
8.2 访问组件方法 #
blade
<div class="{{ $this->classes() }}">
{{ $slot }}
</div>
九、内联组件视图 #
9.1 内联渲染 #
php
class Alert extends Component
{
public $type;
public function render()
{
return <<<'blade'
<div class="alert alert-{{ $type }}">
{{ $slot }}
</div>
blade;
}
}
十、组件注册 #
10.1 自动发现 #
组件默认在 app/View/Components 和 resources/views/components 中自动发现。
10.2 手动注册 #
php
// app/Providers/AppServiceProvider.php
public function boot()
{
Blade::component('alert', Alert::class);
}
blade
<x-alert />
10.3 组件别名 #
php
Blade::component(Alert::class, 'alert-message');
blade
<x-alert-message />
十一、组件包 #
11.1 加载组件视图 #
php
// 服务提供者中
public function boot()
{
$this->loadViewComponentsAs('ui', [
'button' => ButtonComponent::class,
'card' => CardComponent::class,
]);
}
blade
<x-ui-button>按钮</x-ui-button>
<x-ui-card>卡片</x-ui-card>
十二、实战示例 #
12.1 表单输入组件 #
blade
<!-- resources/views/components/input.blade.php -->
@props(['label', 'type' => 'text', 'error'])
<div class="form-group">
<label for="{{ $id ?? $name }}">{{ $label }}</label>
<input
type="{{ $type }}"
name="{{ $name }}"
id="{{ $id ?? $name }}"
value="{{ $value ?? old($name) }}"
{{ $attributes->merge(['class' => 'form-control' . ($error ? ' is-invalid' : '')]) }}
>
@if($error)
<div class="invalid-feedback">{{ $error }}</div>
@endif
</div>
blade
<!-- 使用 -->
<x-input
name="email"
label="邮箱地址"
type="email"
:error="$errors->first('email')"
/>
12.2 模态框组件 #
blade
<!-- resources/views/components/modal.blade.php -->
@props(['id', 'title', 'size' => 'md'])
<div class="modal fade" id="{{ $id }}" tabindex="-1">
<div class="modal-dialog modal-{{ $size }}">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{ $title }}</h5>
<button type="button" class="close" data-dismiss="modal">
<span>×</span>
</button>
</div>
<div class="modal-body">
{{ $slot }}
</div>
<div class="modal-footer">
{{ $footer ?? '' }}
</div>
</div>
</div>
</div>
blade
<!-- 使用 -->
<x-modal id="confirmModal" title="确认删除">
<p>确定要删除这条记录吗?</p>
<x-slot:footer>
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-danger">删除</button>
</x-slot:footer>
</x-modal>
十三、总结 #
13.1 核心要点 #
| 要点 | 说明 |
|---|---|
| @extends | 继承布局 |
| @yield | 定义占位区 |
| @section | 定义/填充区块 |
| @parent | 保留父模板内容 |
| 组件 | <x-component-name> |
| 插槽 | $slot, 具名插槽 |
13.2 下一步 #
掌握了模板继承与组件后,让我们继续学习 Blade指令,了解更多Blade高级指令!
最后更新:2026-03-28