本节摘要:Blade 用两套机制解决"页面越来越像"的问题:模板继承让全站共用同一副骨架(布局定义插座,子页填内容),组件让可复用部件(卡片、弹窗、表单控件)像 HTML 标签一样被使用。本节把语法的常用面过一遍,再用"给工单系统搭全站骨架"的完整演练把两者接起来。
Blade 模板最终编译成原生 PHP,所以它的语法只有薄薄一层需要学:输出、控制流、注释。真正决定项目可维护性的是工艺选择——哪里用继承、哪里用组件。这一节的顺序是先底盘后工艺。
{{-- 输出:自动转义,XSS 的第一道防线(双大括号永远不输出原生 HTML)--}} <p>工单:{{ $order->title }}</p> {{-- 明确不转义才用这一个:内容必须是自己或用户可信来源过滤后的 HTML --}} <div>{!! $order->body_html !!}</div> {{-- 条件与循环 --}} @if ($order->status === 'building') <span class="badge">施工中</span> @elseif ($order->status === 'done') <span class="badge badge-ok">已完工</span> @else <span class="badge badge-muted">待处理</span> @endif @foreach ($orders as $order) <tr> <td>{{ $order->sn }}</td> <td>{{ $order->title }}</td> <td>{{ number_format($order->budget / 100, 2) }}</td> </tr> @endforeach {{-- 空集合的体面写法 --}} @forelse ($orders as $order) <tr><td>{{ $order->title }}</td></tr> @empty <tr><td colspan="3">还没有工单,去接一单吧</td></tr> @endforelse {{-- 原样输出 Blade 语法的场景用 @ 符号逃逸 --}} {{-- @{{ jsVariable }} 会原样输出 @{{ jsVariable }} --}}
底盘里唯一要刻进骨头的规矩:{{ }} 永远转义,{!! !!} 只在内容可信且必要时用。把用户输入用不转义方式输出,是 XSS 攻击的直接入口——第六章后面讲表单,第七章讲安全,反复回到这一条。
工单系统的每个页面都有同样的导航、侧栏、页脚,只有主内容区在变。布局页负责定义骨架和插座:
{{-- resources/views/layouts/app.blade.php --}} <!DOCTYPE html> <html lang="zh"> <head> <meta charset="utf-8"> <title>@yield('title', '工单工地')</title> </head> <body> <nav>工单工地 · @yield('nav-title')</nav> <main> @yield('content') </main> @stack('scripts') </body> </html>
{{-- resources/views/orders/index.blade.php:子页填插座 --}} @extends('layouts.app') @section('title', '工单列表') @section('content') <h1>全部工单</h1> <table> @forelse ($orders as $order) <tr><td>{{ $order->sn }}</td><td>{{ $order->title }}</td></tr> @empty <tr><td>暂无工单</td></tr> @endforelse </table> @endsection @push('scripts') <script src="/js/orders.js"></script> @endpush
yield 是插座,section 是插头;stack 与 push 让子页能往布局的特定位置追加内容(典型如按页面加载不同的脚本)。改菜单只改布局一个文件,这是继承的价值。

继承解决"页与页"的复用,组件解决"页内部件"的复用。告警条、状态徽章、表单输入框这类部件,做成组件后像 HTML 标签一样用:
php artisan make:component StatusBadge
<?php // app/View/Components/StatusBadge.php namespace App\View\Components; use Illuminate\View\Component; use Illuminate\View\View; class StatusBadge extends Component { public function __construct(public string $status) {} public function color(): string { return match($this->status) { 'building' => '#2f7d4f', 'done' => '#5b5b8f', default => '#8a7350', }; } public function render(): View { return view('components.status-badge'); } }
{{-- resources/views/components/status-badge.blade.php --}} <span style="color: {{ $color() }}">{{ strtoupper($status) }}</span> {{-- 使用侧:像标签一样用,属性即构造参数 --}} <x-status-badge status="building" /> <x-status-badge :status="$order->status->value" />
工艺选择的判断标准:整页骨架的复用用继承;跨页面出现的小部件用组件;只在一个页面用一次的东西不急着抽象。组件嵌组件是常态——表单组件里再套输入框组件,就是下一节表单装修的底子。
骨架搭好了,接下来是装修里最考验细心的一段:表单。下一节把回填、报错、CSRF 一次讲透。