Laravel 13 视图系统完全指南

视图是 Laravel 应用中负责展示层的核心组件。本文将深入探讨 Laravel 13 视图系统的高级用法和最佳实践。

视图基础

创建和返回视图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use Illuminate\Support\Facades\View;

class UserController extends Controller
{
public function index()
{
return view('users.index', [
'users' => User::all(),
]);
}

public function show(User $user)
{
return view('users.show')
->with('user', $user)
->with('posts', $user->posts);
}
}

视图存在性检查

1
2
3
4
5
6
7
use Illuminate\Support\Facades\View;

if (View::exists('emails.welcome')) {
return view('emails.welcome');
}

return view('emails.default');

首个可用视图

1
2
3
4
return view()->first([
'custom.users.index',
'users.index',
], ['users' => $users]);

视图数据传递

传递数据到视图

1
2
3
4
5
6
7
return view('greeting', ['name' => 'James']);

return view('greeting')
->with('name', 'James')
->with('age', 30);

return view('greeting')->withName('James');

视图间共享数据

1
2
3
4
5
6
7
8
9
10
use Illuminate\Support\Facades\View;

class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
View::share('company', 'Acme Inc');
View::share('currentYear', date('Y'));
}
}

视图组合器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

namespace App\View\Composers;

use Illuminate\View\View;
use App\Models\Category;

class CategoryComposer
{
protected $categories;

public function __construct(Category $categories)
{
$this->categories = $categories;
}

public function compose(View $view): void
{
$view->with('categories', $this->categories->active()->get());
}
}

注册视图组合器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use App\View\Composers\CategoryComposer;
use Illuminate\Support\Facades\View;

class ViewServiceProvider extends ServiceProvider
{
public function boot(): void
{
View::composer('layouts.sidebar', CategoryComposer::class);

View::composer(['posts.index', 'posts.show'], function ($view) {
$view->with('tags', Tag::popular()->get());
});

View::composer('posts.*', PostComposer::class);

View::composer(['profile', 'dashboard'], ProfileComposer::class);
}
}

视图创建器

1
View::creator('profile', ProfileCreator::class);

Blade 模板引擎

模板继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{{-- resources/views/layouts/app.blade.php --}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>@yield('title', 'Laravel')</title>
@stack('styles')
</head>
<body>
@section('sidebar')
<p>默认侧边栏</p>
@show

<div class="container">
@yield('content')
</div>

@stack('scripts')
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{{-- resources/views/users/index.blade.php --}}
@extends('layouts.app')

@section('title', '用户列表')

@section('sidebar')
@parent
<p>额外的侧边栏内容</p>
@stop

@section('content')
<ul>
@foreach($users as $user)
<li>{{ $user->name }}</li>
@endforeach
</ul>
@stop

@push('styles')
<link rel="stylesheet" href="{{ asset('css/users.css') }}">
@endpush

@push('scripts')
<script src="{{ asset('js/users.js') }}"></script>
@endpush

组件系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php

namespace App\View\Components;

use Illuminate\View\Component;
use App\Models\Alert;

class Alert extends Component
{
public function __construct(
public string $type = 'info',
public ?string $title = null,
public bool $dismissible = false
) {}

public function render()
{
return view('components.alert');
}

public function iconClass(): string
{
return match($this->type) {
'success' => 'fa-check-circle',
'warning' => 'fa-exclamation-triangle',
'danger' => 'fa-times-circle',
default => 'fa-info-circle',
};
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{{-- resources/views/components/alert.blade.php --}}
<div {{ $attributes->merge(['class' => "alert alert-{$type}"]) }}>
@if($title)
<h5 class="alert-heading">
<i class="fa {{ $iconClass() }}"></i>
{{ $title }}
</h5>
@endif

{{ $slot }}

@if($dismissible)
<button type="button" class="close" data-dismiss="alert">
<span>&times;</span>
</button>
@endif
</div>
1
2
3
4
5
6
7
<x-alert type="success" title="操作成功" :dismissible="true">
用户已成功创建!
</x-alert>

<x-alert type="danger" class="mb-4">
<strong>错误:</strong> 操作失败,请重试。
</x-alert>

插槽

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{{-- 组件定义 --}}
<div class="card">
<div class="card-header">
{{ $title }}
</div>
<div class="card-body">
{{ $slot }}
</div>
@if($footer)
<div class="card-footer">
{{ $footer }}
</div>
@endif
</div>

{{-- 使用 --}}
<x-card>
<x-slot:title>
<strong>用户统计</strong>
</x-slot:title>

<p>总用户数:{{ $totalUsers }}</p>

<x-slot:footer>
<small>更新于:{{ now()->format('Y-m-d H:i') }}</small>
</x-slot:footer>
</x-card>

内联组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use Illuminate\View\Component;

class Badge extends Component
{
public function __construct(public string $variant = 'primary') {}

public function render()
{
return <<<'blade'
<span {{ $attributes->merge(['class' => "badge bg-{$variant}"]) }}>
{{ $slot }}
</span>
blade;
}
}

控制结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@if(count($users) > 0)
<p>有用户</p>
@elseif(count($users) === 0)
<p>没有用户</p>
@else
<p>未知状态</p>
@endif

@unless($user->isAdmin())
<p>您不是管理员</p>
@endunless

@isset($user->profile)
<p>{{ $user->profile->bio }}</p>
@endisset

@empty($posts)
<p>暂无文章</p>
@endempty

@auth
<p>欢迎,{{ auth()->user()->name }}</p>
@endauth

@guest
<p>请登录</p>
@endguest

@production
<p>生产环境</p>
@endproduction

@env('local')
<p>本地开发环境</p>
@endenv

循环结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@for($i = 0; $i < 10; $i++)
<p>{{ $i }}</p>
@endfor

@foreach($users as $user)
@if($loop->first)
<p>第一个用户</p>
@endif

<li>{{ $user->name }}</li>

@if($loop->last)
<p>最后一个用户</p>
@endif
@endforeach

@forelse($posts as $post)
<li>{{ $post->title }}</li>
@empty
<p>暂无文章</p>
@endforelse

@while(true)
<p>循环内容</p>
@break($condition)
@continue($condition)
@endwhile

循环变量

1
2
3
4
5
6
7
8
9
10
11
12
@foreach($items as $item)
<p>索引:{{ $loop->index }}</p>
<p>序号:{{ $loop->iteration }}</p>
<p>剩余:{{ $loop->remaining }}</p>
<p>总数:{{ $loop->count }}</p>
<p>是否首个:{{ $loop->first }}</p>
<p>是否末个:{{ $loop->last }}</p>
<p>是否偶数:{{ $loop->even }}</p>
<p>是否奇数:{{ $loop->odd }}</p>
<p>深度:{{ $loop->depth }}</p>
<p>父循环:{{ $loop->parent->iteration }}</p>
@endforeach

表单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<form method="POST" action="{{ route('users.store') }}">
@csrf
@method('PUT')

<div class="form-group">
<label for="name">姓名</label>
<input type="text"
name="name"
id="name"
value="{{ old('name') }}"
class="@error('name') is-invalid @enderror">

@error('name')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>

<button type="submit">提交</button>
</form>

包含子视图

1
2
3
4
5
6
7
8
9
@include('partials.header', ['title' => '首页'])

@includeIf('partials.sidebar')

@includeWhen($user->isAdmin(), 'partials.admin-panel')

@includeFirst(['custom.header', 'partials.header'])

@each('partials.user-item', $users, 'user', 'partials.no-users')

原始 PHP

1
2
3
4
5
6
7
8
@php
$total = 0;
foreach($items as $item) {
$total += $item->price;
}
@endphp

@unset($variable)

视图组件

动态组件

1
2
3
<component :is="$componentName" v-bind="$attributes">
{{ $slot }}
</component>

匿名组件

1
2
3
4
5
6
7
8
9
{{-- resources/views/components/button.blade.php --}}
<button {{ $attributes->merge(['class' => 'btn']) }}>
{{ $slot }}
</button>

{{-- 使用 --}}
<x-button type="submit" class="btn-primary">
提交
</x-button>

组件属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{{-- 组件 --}}
<div {{ $attributes->merge(['class' => 'panel']) }}>
{{ $slot }}
</div>

{{-- 使用 --}}
<x-panel class="bg-white" data-id="123">
内容
</x-panel>

{{-- 输出 --}}
<div class="panel bg-white" data-id="123">
内容
</div>

属性筛选

1
2
3
4
5
6
7
8
9
10
11
public function render()
{
return <<<'blade'
<div>
<input type="text" {{ $attributes->only(['class', 'placeholder']) }}>
<span {{ $attributes->except(['class']) }}>
{{ $slot }}
</span>
</div>
blade;
}

高级视图技巧

视图缓存

1
2
php artisan view:cache
php artisan view:clear

自定义 Blade 指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use Illuminate\Support\Facades\Blade;

class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
Blade::directive('datetime', function ($expression) {
return "<?php echo ($expression)->format('Y-m-d H:i:s'); ?>";
});

Blade::directive('currency', function ($expression) {
return "<?php echo number_format($expression, 2); ?>";
});

Blade::if('admin', function () {
return auth()->check() && auth()->user()->isAdmin();
});

Blade::if('env', function ($environment) {
return app()->environment($environment);
});
}
}
1
2
3
4
5
6
7
8
9
10
@datetime($user->created_at)
@currency($order->total)

@admin
<p>管理员内容</p>
@endadmin

@env('local')
<p>调试信息</p>
@endenv

可渲染对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php

namespace App\View;

use Illuminate\Contracts\Support\Renderable;

class PdfDocument implements Renderable
{
public function __construct(
protected string $content,
protected string $title
) {}

public function render(): string
{
return view('documents.pdf', [
'content' => $this->content,
'title' => $this->title,
])->render();
}
}

// 使用
return new PdfDocument($content, 'Document Title');

总结

Laravel 13 的视图系统提供了:

  • 灵活的模板继承和组件系统
  • 强大的 Blade 模板引擎
  • 视图组合器和创建器
  • 自定义指令扩展
  • 高性能视图缓存

掌握视图系统是构建优雅用户界面的基础。