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>×</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 模板引擎
- 视图组合器和创建器
- 自定义指令扩展
- 高性能视图缓存
掌握视图系统是构建优雅用户界面的基础。