~/blog/laravel-blade-template-architecture-best-practices.md
Laravel 與後端開發 · 2025 / 12 / 03

Blade 模板是藝術品還是災難現場?Laravel 8 個關鍵實踐,打造可維護、高效能的視圖架構

Eric — 浪花科技創辦人 / AI 架構師
Eric
浪花科技創辦人 · AI 架構師
Blade 模板是藝術品還是災難現場?Laravel 8 個關鍵實踐,打造可維護、高效能的視圖架構
目錄 table-of-contents.md

視圖層常被當成 Laravel 專案裡「不就是輸出 HTML」的二等公民,結果它反而成了技術債的重災區:Controller 肥大、Blade 裡塞滿商業邏輯,三個月後回頭看連自己都想打自己。模板可以是藝術品,也可以是災難現場,差別就在這 8 個關鍵實踐。

別鬧了,一個好的專案,從後端架構到前端模板都應該是清晰、優雅且高效的。今天,我們不談 WordPress 的 a-actions 和 f-filters,來聊聊隔壁棚 Laravel 的 Blade 模板引擎。Blade 的簡潔語法底下,其實蘊含著深刻的軟體架構思維。這篇文章不是要給你一堆複製貼上的程式碼片段,而是要帶你從架構層面,徹底搞懂 Laravel Blade 模板最佳實務,讓你的視圖層不再是專案的負擔,而是堅實的基石。

一、停止在 Blade 中濫用 @php:視圖的唯一職責是「呈現」

這是我最想囉嗦的一點,也是新手最常犯的錯。看到 @php 就手癢,想在 Blade 裡面做一堆資料處理、邏輯判斷,甚至...下資料庫查詢?拜託,快住手!

MVC 架構的核心精神就是「關注點分離」(Separation of Concerns)。

  • Model: 處理資料與商業邏輯。
  • View: 負責呈現資料,越笨越好。
  • Controller: 扮演兩者之間的橋樑,調度 Model 準備好資料,再餵給 View。

當你在 Blade 裡寫下複雜的邏輯,你等於是打破了這個原則。這會導致:

  • 難以測試: 你要怎麼對 Blade 裡的邏輯寫單元測試?幾乎不可能。
  • 難以維護: 商業邏輯散落在 Controller 和 View 各處,尋找和修改都像在尋寶,而且是那種會爆炸的寶藏。
  • 效能問題: 在 View 裡面執行非預期的資料庫查詢,很容易造成 N+1 問題,拖垮整個頁面效能。

工程師小囉嗦: 你的 Blade 應該要「笨」到只會做簡單的 if/elseforeach 迴圈,以及顯示從 Controller 傳過來的變數。所有資料的整理、計算、篩選,都應該在 Controller 或更下層的 Service/Repository 完成。把乾淨、準備好的資料「盤子」端給 Blade,它只需要負責把菜擺上桌就好。

反面教材:

<!-- 這會讓未來的你很痛苦 -->
@php
    $activeUsers = App\Models\User::where('status', 'active')->where('last_login_at', '>', now()->subDays(30))->get();
    $premiumCount = 0;
    foreach($activeUsers as $user) {
        if ($user->isPremium()) {
            $premiumCount++;
        }
    }
@endphp

<p>活躍的白金會員數:{{ $premiumCount }}</p>

最佳實踐:

<!-- 在 Controller 中準備好資料 -->
// In YourController.php
public function showDashboard()
{
    $premiumCount = $this->userService->getActivePremiumCount();
    return view('dashboard', ['premiumCount' => $premiumCount]);
}

<!-- 在 Blade 中,只做呈現 -->
<!-- dashboard.blade.php -->
<p>活躍的白金會員數:{{ $premiumCount }}</p>

二、擁抱組件化 (Components):告別 @include 義大利麵

當你的專案開始變大,你會發現很多 UI 元素(按鈕、卡片、表單輸入框)不斷重複出現。很多人的直覺是使用 @include。這在初期是可行的,但當你需要傳遞一堆變數,而且巢狀結構越來越深時,很快就會變成一場災難。

Laravel 的 Blade 組件 (Components) 是解決這個問題的利器。它讓你可以像使用 Vue 或 React 組件一樣,建立可重複使用、自給自足的 UI 單元。每個組件都有自己的 Class 處理邏輯,和自己的 Blade 檔案處理樣式。

為什麼組件優於 @include?

  • 語意清晰: <x-forms.button type="primary">送出</x-forms.button> 遠比 @include('partials.button', ['type' => 'primary', 'text' => '送出']) 來得直觀。
  • 邏輯封裝: 你可以在組件的 Class 中處理複雜的顯示邏輯(例如,根據不同狀態顯示不同顏色或 icon),讓 Blade 檔案保持乾淨。
  • 資料隔離: 組件只接收明確傳入的 props,不會意外地污染或被外部變數影響。
  • 插槽 (Slots): 組件提供了強大的 $slot 功能,讓你可以輕鬆地將內容嵌入到組件的預定位置,增加了極大的彈性。

想像一下,一個 Modal 彈窗組件,你可以這樣優雅地使用它:

<x-modal title="確認刪除">
    <x-slot name="body">
        <p>你確定要刪除這筆資料嗎?此操作無法復原。</p>
    </x-slot>

    <x-slot name="footer">
        <x-forms.button type="secondary">取消</x-forms.button>
        <x-forms.button type="danger">確認刪除</x-forms.button>
    </x-slot>
</x-modal>

工程師小囉嗦: 這就像是在組裝一台電腦。你不會把 CPU、記憶體、硬碟的針腳全部焊死在主機板上(@include 的極端比喻)。你會使用標準化的插槽,讓每個零件(組件)都可以獨立運作、輕鬆替換。從今天起,把「建立可複用的 UI」這個念頭,從 @include 轉換到 Blade Components 吧!

三、善用 @inject 和 View Composers 注入依賴

有時候,某些資料在很多頁面都會用到,例如頁首的導覽列、頁尾的網站資訊、側邊欄的最新文章等。你總不希望在每個 Controller 的每個方法裡都去重複查詢這些資料吧?

這時候,你有兩個好幫手:

1. 視圖合成器 (View Composers)

View Composers 就像是針對特定 Blade 模板的「幕後工作人員」。你可以在 Service Provider 中註冊一個 Composer,告訴 Laravel:「嘿,當任何地方要渲染 `layouts.header` 這個模板時,請先執行這段程式碼,並把結果綁定到模板上。」

// In a ServiceProvider, e.g., AppServiceProvider.php
use Illuminate\Support\Facades\View;

public function boot()
{
    View::composer('layouts.header', function ($view) {
        $categories = Category::where('is_active', true)->get();
        $view->with('categories', $categories);
    });
}

這樣一來,所有使用 `layouts.header` 的地方,都會自動獲得 $categories 這個變數,完全不需要 Controller 的介入。

2. @inject 指令

如果你只是在單一模板中需要用到某個 Service 的方法,而且這個需求不具備全域性,@inject 是一個更輕量的選擇。它允許你直接在 Blade 模板的開頭,從 Laravel 的服務容器 (Service Container) 中解析出一個實例。

@inject('metrics', 'App\Services\MetricsService')

<div>
    今日訪客數: {{ $metrics->getTodayVisitors() }}
</div>

工程師小囉嗦: 兩者怎麼選?我的原則是:如果資料是跨越多個頁面的「共享佈局」元件(如頁首、頁尾),用 View Composer;如果只是某個特定頁面或大型組件需要呼叫一些輔助方法,用 @inject。但切記,別在 @inject 進來的 Service 方法裡做太複雜的商業邏輯,那應該是 Controller 的工作。

四、其他關鍵實踐,讓你的 Blade 更上一層樓

  • @once@push 當你有一個組件需要在頁面中多次使用,但它依賴的 JS 或 CSS 只需要載入一次時,@once 指令是你的救星。搭配 @push,可以將組件的資源集中推送到主佈局的特定堆疊 (stack) 中,避免重複載入。
  • @props 在組件的 Blade 檔案中,使用 @props 明確定義你期望接收的屬性,可以設定預設值,並將其餘屬性合併到 HTML 標籤上,非常方便。
  • 保持安全性: 永遠優先使用 {{ $variable }} 語法,Blade 會自動對內容進行 HTML 編碼,防止 XSS 攻擊。只有在你 100% 確定內容來源安全(例如,來自後台富文本編輯器且經過處理的內容)時,才謹慎使用 {!! $variable !!}
  • 利用迴圈變數 $loop@foreach 迴圈中,善用 Blade 自動提供的 $loop 變數,你可以輕易判斷是否為第一次/最後一次迭代 ($loop->first, $loop->last),取得索引 ($loop->index) 等,不需要自己再額外設定計數器。

寫出好的 Blade 模板,不只關乎程式碼的美觀,它直接影響到整個專案的健康度、可維護性和團隊協作效率。當你的視圖層清晰、模組化且職責單一時,你會發現新增功能、修改設計都變得輕而易舉。別再把 Blade 當成一個只能放 HTML 的地方了,把它當成你精心設計的應用程式架構中,優雅的最後一哩路吧!

延伸閱讀

如果你正在為你的 Laravel 或 WordPress 專案的架構感到頭痛,或是希望能導入更現代、更高效的開發實踐,浪花科技的團隊擁有豐富的實戰經驗,能幫助你從源頭打造穩固、可擴展的數位產品。我們不只是寫程式,我們打造能為你創造價值的系統。

準備好讓你的專案脫胎換骨了嗎?

👉 立即聯繫浪花科技,讓我們聊聊如何將你的想法,打造成兼具效能與優雅架構的成功專案!

// FAQ

常見問題

可以在 Laravel Blade 模板裡用 @php 寫資料查詢或商業邏輯嗎?
不建議。Blade 屬於 MVC 架構中的 View 層,職責只有「呈現」,應保持單純,只做 if/else、foreach 與顯示變數。資料的查詢、計算與篩選應在 Controller 或更下層的 Service/Repository 完成;在 View 內執行資料庫查詢容易造成難以測試、難以維護,並引發 N+1 效能問題。
Blade Components 和 @include 有什麼差別,該用哪一個?
Blade Components 是可重複使用、自給自足的 UI 單元,擁有自己的 Class 處理邏輯與獨立的 props,並支援 Slots 嵌入內容,語意更清晰、邏輯可封裝、資料彼此隔離。@include 在需要傳遞大量變數或巢狀結構變深時容易失控,因此建立可複用 UI 時優先選用 Components。
View Composer 和 @inject 該如何選擇?
如果資料是跨多個頁面的共享佈局元件(例如頁首、頁尾、側邊欄),用 View Composer,在 Service Provider 註冊後即會自動綁定變數到指定模板,不需 Controller 介入。如果只是某個特定頁面或大型組件需要呼叫輔助方法,則用較輕量的 @inject 直接從服務容器解析實例。
如何讓頁首導覽列等共用資料自動帶入模板,不必每個 Controller 重複查詢?
使用 View Composer。在 Service Provider 的 boot 方法中用 View::composer 綁定特定模板(如 layouts.header),當該模板被渲染時就會自動執行查詢並把資料附加到視圖上。這樣所有使用該模板的頁面都能自動取得資料,無需在每個 Controller 重複撈取。
~/roamer-tech/newsletter // FREE
// newsletter

訂閱免費電子報

把 AI 自動化、企業系統設計與 WordPress / Laravel 開發的真實案例和可直接照做的技巧,整理成電子報寄給你。只寄精選內容、不灌垃圾信,一鍵就能退訂。

$
// final.exec()

準備好讓你的網站開始為你工作了嗎?