> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# 套件 View 的覆寫與更新

> 從 Laravel 13 的實作解說帶命名空間 View 的搜尋順序、已公開 Blade 範本的維護，以及 View 快取與搜尋結果快取的差異。

在讓使用者自訂畫面或郵件範本的套件中，除了公開 View 之外，還需要一套能在保留已公開檔案的情況下進行更新的約定。即使修正了套件端的 Blade 檔案，使用端應用程式也不一定會渲染該檔案。

本頁以[套件開發的基礎](/zh-TW/advanced/package-development)為前提，將 View 的選擇、檔案的公開與快取分開來思考。官方文件參照 Laravel 13，框架的實作參照最新版本 `v13.34.0`。

## 註冊與公開是不同的處理

`loadViewsFrom()` 會將搜尋路徑註冊至命名空間。`publishes()` 會註冊複製來源與複製目的地，實際的複製則由 `vendor:publish` 執行。在下列範例中，即使不公開也能使用 `courier::deliveries.show`。

```php src/CourierServiceProvider.php theme={null}
<?php

namespace Acme\Courier;

use Illuminate\Support\ServiceProvider;

class CourierServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');

        $this->publishes([
            __DIR__.'/../resources/views' => resource_path('views/vendor/courier'),
        ], 'courier-views');
    }
}
```

套件端的檔案放在 `resources/views/deliveries/show.blade.php`。View 名稱中的點會在搜尋時轉換為目錄分隔符號。

```php theme={null}
return view('courier::deliveries.show', [
    'trackingCode' => 'TRACK-001',
]);
```

```blade resources/views/deliveries/show.blade.php theme={null}
<p>追蹤編號: {{ $trackingCode }}</p>
```

View 的命名空間與 Composer 的套件名稱或 PHP 的命名空間無關。在此，`loadViewsFrom()` 第 2 個引數所指定的 `courier`，就是 View 參照與覆寫目錄的約定。

## 以檔案為單位尋找覆寫位置

`ServiceProvider::loadViewsFrom()` 會在 `view` 被解析時，依序確認設定中的 `view.paths`。若各路徑下存在 `vendor/courier` 目錄，就將該目錄加入命名空間，最後再加入套件端的路徑。

`FileViewFinder` 會依序搜尋該命名空間的路徑，並回傳最先找到的檔案。在使用標準 `resources/views` 的配置下，順序如下。

```mermaid theme={null}
flowchart TD
    A["courier::deliveries.show"] --> B["搜尋 resources/views/vendor/courier/<br>deliveries/show.blade.php"]
    B --> C{"檔案存在?"}
    C -->|是| D["使用使用端應用程式的 View"]
    C -->|否| E["搜尋套件的 resources/views/<br>deliveries/show.blade.php"]
    E --> F{"檔案存在?"}
    F -->|是| G["使用套件的 View"]
    F -->|否| H["找不到 View 的例外"]
```

這並不是切換整個目錄。即使使用者只覆寫了 `deliveries/show.blade.php`，其他未覆寫的 View 仍會從套件端載入。

| 使用端應用程式的狀態 | 選用的 View |
| - | - |
| 沒有覆寫檔案 | 套件的檔案 |
| 存在相同相對路徑的覆寫檔案 | 使用端應用程式的檔案 |
| 只移除了覆寫檔案 | 在新的啟動中回到套件的檔案 |
| 兩邊都沒有目標檔案 | `View [...] not found.` 例外 |

<Info>
  在有多個 `view.paths` 的配置中，覆寫位置也可能有多個。`resource_path('views/vendor/courier')` 只是此範例的公開目的地，並不是將搜尋對象固定在該處的處理。請讓命名空間保持套件專屬，並避免由多個服務提供者對同一名稱加入路徑的設計。
</Info>

## 只自訂需要的 View

使用者可以透過下列指令複製範本。請指定服務提供者與標籤，避免連同其他資源一起公開。

```bash theme={null}
php artisan vendor:publish --provider="Acme\Courier\CourierServiceProvider" --tag=courier-views
```

此註冊會公開整個 View 目錄。若不需要覆寫全部內容，也可以在確認內容後只保留要自訂的檔案，或只將需要的檔案手動複製到相同的相對路徑。這是因為即使是未經編輯的副本，只要存在就會被視為覆寫。

<Warning>
  已公開的範本不會隨套件更新自動同步。在舊副本優先的狀態下只修正套件端，該 View 的變更不會反映出來。顯示問題的修正或表單變更等，也需要與覆寫檔案進行比對。
</Warning>

### 重新公開不會合併差異

`VendorPublishCommand` 通常會在同名的公開目的地檔案存在時略過複製。`--force` 會覆寫既有檔案。此外，`--existing` 也是「覆寫已公開檔案」的選項，而不是保留使用者編輯內容的模式。

| 操作 | 對 View 檔案的影響 |
| - | - |
| 一般的重新公開 | 保留既有檔案，複製不存在的目標檔案 |
| 加上 `--force` 公開 | 連既有的自訂內容也會覆寫 |
| 加上 `--existing` 公開 | 只覆寫公開目的地中已存在的目標檔案 |

任何一種方式都不是比對舊版、新版與使用者編輯內容後的合併，也不會自動從公開目的地移除已從套件刪除的 View。請不要讓更新步驟只是「重新公開同一個標籤」。

## 將 View 也當作公開 API 來維護

不只是 View 名稱，接收的資料與參照的元件也會影響使用者的自訂內容。例如，若新版將 `trackingCode` 改為其他變數名稱，保留舊範本的使用者將無法從新程式碼取得所需的值。

發行前，請確認下列約定。

* 不要隨意變更命名空間以及 `deliveries.show` 等 View 名稱。
* 記錄傳入的變數、型別，以及必填與選填的區別。
* 將 `@include` 與 `@extends` 的參照對象、Blade 元件的 props 也納入變更項目。
* 在發行說明中記載修正過的 View，以及應套用至已公開舊版的變更。

為使用者準備一套比對舊版與新版套件 View、並將必要變更手動合併至已自訂檔案的步驟。對於不再需要覆寫的檔案，先透過備份或版本控制保全變更後再移除，即可回到套件端的 View。

## Blade 快取不會更新覆寫檔案

`view:cache` 會將 Blade 範本預先編譯為 PHP。`ViewCacheCommand` 會先執行 `view:clear`，再收集一般的 View 路徑與註冊至命名空間的路徑，找出編譯對象。

```bash theme={null}
php artisan view:cache
```

此處理不會改寫已公開的 Blade 檔案，也不會改變 View 的搜尋優先順序。若存在舊的覆寫檔案，即使重建快取，仍會繼續選用該檔案。部署時，請先完成程式碼與覆寫檔案的更新再進行編譯。

開發中若想刪除已編譯的檔案並重新渲染，請使用下列指令。

```bash theme={null}
php artisan view:clear
```

若一般的時間戳記檢查已啟用，Blade 編譯器會比較原始檔案與已編譯檔案的更新時間。不過，也有停用時間戳記檢查的配置，因此請不要將部署時的重建完全交給自動判斷。

### 與搜尋結果的快取區分開來

`FileViewFinder::find()` 會將找到的路徑儲存在該 Finder 實例的 `$views` 陣列中。此外，覆寫目錄是否存在的確認是在 `loadViewsFrom()` 的回呼中進行。即使在啟動後新增目錄，也不會自動加入已註冊的搜尋路徑。

| 管理對象 | 角色 | 更新時的思考方式 |
| - | - | - |
| 已公開的 Blade 檔案 | 使用者的自訂內容 | 合併差異，或停止覆寫 |
| 已編譯的 PHP | Blade 的編譯結果 | 以 `view:cache` / `view:clear` 管理 |
| Finder 的註冊路徑與搜尋結果 | 執行中實例的 View 選擇 | 以新的程式碼與配置重新啟動長時間執行的處理程序 |

`view:clear` 並不是一次清除其他執行中處理程序所持有之 Finder 狀態的指令。在 Octane 等長時間執行的處理程序中，請依照一般的部署步驟重新載入。Finder 的 `flush()` 會清除搜尋結果，但不會進一步註冊新的覆寫目錄。

## 發行前應確認的事項

除了套件的測試之外，也請在使用端應用程式中確認下列組合。只渲染最新範本的測試，無法驗證對已公開舊版之使用者的相容性。

* 在未公開的狀態下，會渲染套件的 View。
* 只覆寫 1 個檔案時，只有該檔案優先，其他檔案會退回使用套件的 View。
* 即使保留舊版已公開的範本，也能以新版傳入的資料進行渲染。
* 一般的重新公開會保留自訂內容，未公開檔案的新增也符合預期。
* 變更覆寫檔案後 `view:cache` 能成功執行，且在新的啟動中顯示變更後的內容。

## 相關頁面

<Columns cols={2}>
  <Card title="View" icon="eye" href="/zh-TW/views">
    確認 View 的建立、資料傳遞與預先編譯的基礎。
  </Card>

  <Card title="Blade 範本" icon="code" href="/zh-TW/blade">
    確認版面配置、include 與元件的使用方式。
  </Card>

  <Card title="版本相容性管理" icon="code-branch" href="/zh-TW/advanced/package-versioning">
    將範本的約定變更連結至發行方針。
  </Card>

  <Card title="Octane" icon="bolt" href="/zh-TW/octane">
    確認長時間執行之應用程式的生命週期與重新載入。
  </Card>
</Columns>

## 參考的一手資料

* [Laravel 官方文件：套件的 View](https://github.com/laravel/docs/blob/156fc7fde114548640e13c39aa79b991291c3f91/packages.md#views)
* [ServiceProvider：View 路徑的註冊](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Support/ServiceProvider.php)
* [FileViewFinder：搜尋順序與搜尋結果的保存](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/View/FileViewFinder.php)
* [VendorPublishCommand：檔案的公開條件](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Foundation/Console/VendorPublishCommand.php)
* [ViewCacheCommand：View 路徑的收集與編譯](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Foundation/Console/ViewCacheCommand.php)
* [ViewClearCommand：已編譯檔案的刪除](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Foundation/Console/ViewClearCommand.php)
* [Compiler：更新時間的確認](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/View/Compilers/Compiler.php)


## Related topics

- [Laravel 套件開發](/zh-TW/advanced/package-development.md)
- [套件 Migration 的公開與更新](/zh-TW/advanced/package-migrations.md)
- [Session 恢復](/zh-TW/packages/laravel-copilot-sdk/resume.md)
- [Google Sheets API for Laravel](/zh-TW/packages/laravel-google-sheets/index.md)
- [套件設定的合併與快取](/zh-TW/advanced/package-config-merging.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.