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

# 套件的公開資源與更新

> 解讀 Laravel 13 的公開處理，從長期維護的角度說明 JavaScript 與 CSS 的發布、tag 的選取範圍、覆寫選項，以及 Composer 更新時的重新公開。

即使更新了套件的 JavaScript 或 CSS，已複製到應用程式 `public` 中的檔案也不會自動改變。為了避免只有 PHP 程式碼變成新版、瀏覽器卻持續使用舊版資源的狀況，必須決定公開目的地的擁有者與更新步驟。

本頁以[套件開發的基礎](/zh-TW/advanced/package-development)為前提，從 Laravel 13 的公開處理整理發布與維護的設計。實作的確認使用 `laravel/framework` 的 `v13.35.0`。

## 公開既不是建置也不是同步

`ServiceProvider::publishes()` 會註冊複製來源與複製目的地。實際複製檔案的是 `vendor:publish`。它不會進行 JavaScript 的轉譯、CSS 的建置，也不會加入應用程式的 Vite 進入點。

若由套件端發布已建置的檔案，可採用例如下列的結構。

```text theme={null}
courier/
├── public/
│   ├── courier.css
│   └── courier.js
└── src/
    └── CourierServiceProvider.php
```

```php theme={null}
<?php

namespace Acme\Courier;

use Illuminate\Support\ServiceProvider;

class CourierServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        if ($this->app->runningInConsole()) {
            $this->publishes([
                __DIR__.'/../public' => public_path('vendor/courier'),
            ], 'courier-assets');
        }
    }
}
```

首次公開時，請明確指定提供者與 tag。

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

此範例會建立 `public/vendor/courier/courier.css` 與 `courier.js`。若設計為以一般 CSS 與 JavaScript 發布，可在 Blade 中如下參照。

```blade theme={null}
<link rel="stylesheet" href="{{ asset('vendor/courier/courier.css') }}">
<script src="{{ asset('vendor/courier/courier.js') }}" defer></script>
```

若以 ES modules 等形式發布，請配合發布形式調整載入方式。`asset()` 是產生 URL 的 helper，並不會進行建置、公開，或依內容產生檔名。

<Warning>
  複製來源中只放置可以公開的建置成果。此範例的公開目的地是可從 Web 存取的 `public`。請勿將設定檔或內部資料納入同一個公開群組。
</Warning>

## tag 不是提供者專屬的命名空間

`ServiceProvider` 會將公開路徑註冊到依提供者類別區分的陣列，以及依 tag 區分的陣列。依 tag 區分的陣列由多個提供者共用，因此若使用 `public` 這類通用 tag，其他套件也可能成為對象。

| 指定 | 被選取的公開路徑 |
| - | - |
| `--tag=courier-assets` | 註冊了該 tag 的所有提供者的路徑 |
| `--provider="Acme\Courier\CourierServiceProvider"` | 該提供者註冊的所有路徑 |
| 同時指定提供者與 tag | 該提供者的路徑與該 tag 的路徑的交集 |
| `--all` | 所有提供者的公開路徑 |

同時指定兩者時，`pathsForProviderAndGroup()` 會**以複製來源路徑為鍵**使用 `array_intersect_key()`。這並不是依 tag 切換到不同複製目的地的機制。請避免多次註冊同一個複製來源、讓它依用途擁有不同複製目的地的設計。

`--tag` 可以重複指定，此時會依序公開各個 tag。`--all` 會在選取處理的開頭就返回，因此即使同時加上 `--provider` 或 `--tag`，也不會用於篩選。

<Tip>
  為了不連使用者的設定或 View 都覆寫，更新步驟中請使用套件專屬的資源 tag。若只指定提供者並加上 `--force`，同一提供者的設定與 View 也可能成為對象。
</Tip>

## 區分使用重新公開的選項

`VendorPublishCommand` 的檔案公開與目錄公開，會依複製目的地檔案是否存在以及選項來判斷是否複製。下表是針對存在於複製來源中的一般資源檔案的行為。

| 選項 | 複製目的地不存在的檔案 | 複製目的地已存在的檔案 |
| - | - | - |
| 無 | 新增 | 保留 |
| `--force` | 新增 | 覆寫 |
| `--existing` | 不新增 | 覆寫 |
| `--existing --force` | 不新增 | 覆寫 |

`--existing` 並不是保護編輯內容的選項。它會覆寫既有檔案，卻不會公開新版中新增的檔案。若 JavaScript 的更新需要新增的檔案，只使用 `--existing` 可能無法備齊所有成果。

若約定由套件管理公開目的地、使用者不直接編輯，則在更新後執行下列指令。

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

<Warning>
  `--force` 不會合併差異，也會覆寫使用者的編輯。使用者要自訂的 CSS 請以另一個檔案載入等方式，與套件管理的成果分開。更新方針也要與設定或 View 的自訂區分開來。
</Warning>

### 已刪除的檔案會殘留在公開目的地

目錄公開的 `moveManagedFiles()` 會掃描複製來源中的檔案並寫入。它沒有找出只存在於公開目的地的檔案並加以刪除的處理。`--force` 也不會使目錄完全同步。

例如即使在新版中刪除了 `legacy.js`，只要已公開過舊版，`public/vendor/courier/legacy.js` 仍會殘留。重新命名時舊名稱的檔案同樣會殘留，因此請在發行說明中記錄已刪除或重新命名的檔案，以及參照目標的變更。

若提供移除舊檔案的步驟，請具體列出套件所擁有的檔案。不要設計成整個刪除可能放有使用者自有檔案的目錄。

## 加入 laravel-assets 代表接受覆寫的約定

Laravel 13 的官方應用程式範本中，`composer.json` 的 `post-update-cmd` 有下列腳本。

```json theme={null}
{
    "scripts": {
        "post-update-cmd": [
            "@php artisan vendor:publish --tag=laravel-assets --ansi --force"
        ]
    }
}
```

這是應用程式端的腳本。並不是套件自動偵測本身在更新公開檔案。既有應用程式中腳本也可能已被變更或刪除，因此請確認使用端的設定。

若要加入此更新路徑，請將前面 `publishes()` 的第 2 個參數改為陣列，將同一份資源註冊到 2 個 tag。

```php theme={null}
$this->publishes([
    __DIR__.'/../public' => public_path('vendor/courier'),
], ['courier-assets', 'laravel-assets']);
```

`laravel-assets` 並不是具有特殊複製處理的 tag。由於範本的腳本會以 `--force` 公開該 tag，加入的檔案會在 Composer 更新時成為覆寫對象。請勿註冊使用者會編輯的設定或 View。

<Info>
  自動更新的前提是：應用程式端有該腳本、該事件會被執行，且提供者已註冊公開路徑。為了讓不符合這些前提的部署也能更新，請一併說明使用套件專屬 tag 的重新公開指令。
</Info>

## 在部署中讓 PHP 與資源的版本一致

```mermaid theme={null}
flowchart TD
    A["更新套件"] --> B["註冊公開路徑"]
    B --> C["以專屬 tag 執行 vendor:publish --force<br>或 laravel-assets 的更新腳本"]
    C --> D["新增新檔案<br>覆寫既有檔案"]
    D --> E["整理指定的舊檔案<br>套用瀏覽器與 CDN 的快取方針"]
    E --> F["確認 PHP 與資源以相同版本運作"]
```

`config:cache` 與 `view:cache` 不會改寫已公開的 JavaScript 與 CSS。若設計為公開後仍以相同 URL 提供，瀏覽器或 CDN 的快取可能導致使用舊內容。請將反映發布物版本的 URL 或快取失效等應用程式的提供方針也納入更新步驟。

每次發布時請確認下列組合。

* 在尚未公開的應用程式中，所有必要的已建置檔案都會被公開。
* 在已公開舊版的狀態下，`--force` 會更新既有檔案並新增新檔案。
* 資源更新不會覆寫使用者的設定、View 與自有 CSS。
* 已刪除或重新命名檔案的處理方式已明確說明，且沒有殘留對舊版的參照。
* 透過實際的提供 URL 能取得新版內容，且 PHP 與瀏覽器端的處理能互相配合。

## 相關頁面

<Columns cols={2}>
  <Card title="套件自動偵測" icon="magnifying-glass" href="/zh-TW/advanced/package-discovery">
    確認 Composer 更新、提供者偵測與檔案公開之間的差異。
  </Card>

  <Card title="套件 View 的覆寫與更新" icon="eye" href="/zh-TW/advanced/package-views">
    確認使用者自訂範本的維護方針。
  </Card>

  <Card title="套件快取與 optimize 的整合" icon="gears" href="/zh-TW/advanced/package-optimization">
    解說與檔案公開分開管理的套件快取。
  </Card>

  <Card title="版本相容性管理" icon="code-branch" href="/zh-TW/advanced/package-versioning">
    將公開目的地或發布形式的變更視為相容性的約定來處理。
  </Card>
</Columns>

## 參考的一手資料

* [Laravel 官方文件：Public assets](https://github.com/laravel/docs/blob/13.x/packages.md#public-assets)
* [Laravel 官方文件：Publishing file groups](https://github.com/laravel/docs/blob/13.x/packages.md#publishing-file-groups)
* [Laravel Framework v13.35.0：ServiceProvider](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Support/ServiceProvider.php) — `publishes()`、`addPublishGroup()`、`pathsToPublish()`、`pathsForProviderAndGroup()`。
* [Laravel Framework v13.35.0：VendorPublishCommand](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Foundation/Console/VendorPublishCommand.php) — 選取範圍、覆寫條件、目錄內的複製處理。
* [Laravel 13 官方應用程式範本：composer.json](https://github.com/laravel/laravel/blob/13.x/composer.json) — 透過 `post-update-cmd` 重新公開 `laravel-assets`。


## Related topics

- [Laravel 套件開發](/zh-TW/advanced/package-development.md)
- [進階主題](/zh-TW/advanced/index.md)
- [套件 Migration 的公開與更新](/zh-TW/advanced/package-migrations.md)
- [套件 View 的覆寫與更新](/zh-TW/advanced/package-views.md)
- [套件的靜態分析（PHPStan / Larastan）](/zh-TW/advanced/package-static-analysis.md)


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