跳到主要內容

Asset 組件

編輯此頁面

Asset 組件管理網頁資源(例如 CSS 樣式表、JavaScript 檔案和圖像檔案)的 URL 生成和版本控制。

過去,網頁應用程式通常會硬編碼網頁資源的 URL。例如

1
2
3
4
5
<link rel="stylesheet" type="text/css" href="/css/main.css">

<!-- ... -->

<a href="/"><img src="/images/logo.png" alt="logo"></a>

除非網頁應用程式非常簡單,否則不再建議這種做法。硬編碼 URL 可能會有以下缺點:

  • 範本變得冗長:您必須為每個資源編寫完整路徑。使用 Asset 組件時,您可以將資源分組到套件中,以避免重複路徑的共同部分;
  • 版本控制很困難:必須為每個應用程式自訂管理。在資源 URL 中加入版本(例如 main.css?v=5)對於某些應用程式至關重要,因為它讓您可以控制資源的快取方式。Asset 組件允許您為每個套件定義不同的版本控制策略;
  • 移動資源位置既麻煩又容易出錯:它要求您仔細更新所有範本中包含的所有資源的 URL。Asset 組件允許您輕鬆移動資源,只需變更與資源套件關聯的基礎路徑值即可;
  • 幾乎不可能使用多個 CDN:此技術要求您為每個請求隨機變更資源的 URL。Asset 組件提供對任何數量的多個 CDN 的開箱即用支援,包括常規 (http://) 和安全 (https://) CDN。

安裝

1
$ composer require symfony/asset

注意

如果您在 Symfony 應用程式之外安裝此組件,則必須在您的程式碼中引入 vendor/autoload.php 檔案,以啟用 Composer 提供的類別自動載入機制。請閱讀這篇文章以了解更多詳細資訊。

用法

Asset 套件

Asset 組件透過套件管理資源。一個套件將所有具有相同屬性的資源分組:版本控制策略、基礎路徑、CDN 主機等。在以下基本範例中,建立了一個套件來管理沒有任何版本控制的資源

1
2
3
4
5
6
7
8
9
10
11
12
use Symfony\Component\Asset\Package;
use Symfony\Component\Asset\VersionStrategy\EmptyVersionStrategy;

$package = new Package(new EmptyVersionStrategy());

// Absolute path
echo $package->getUrl('/image.png');
// result: /image.png

// Relative path
echo $package->getUrl('image.png');
// result: image.png

套件實作 PackageInterface,它定義了以下兩個方法

getVersion()
傳回資源的資源版本。
getUrl()
傳回絕對或根相對路徑的公開路徑。

使用套件,您可以

A) 版本化資源;B) 為資源設定通用基礎路徑(例如 /css);C) 為資源設定 CDN

版本化 Asset

Asset 組件的主要功能之一是能夠管理應用程式資源的版本控制。資源版本通常用於控制這些資源的快取方式。

Asset 組件不依賴簡單的版本機制,而是允許您透過 PHP 類別定義進階的版本控制策略。兩種內建策略是 EmptyVersionStrategy,它不會為資源加入任何版本;以及 StaticVersionStrategy,它允許您使用格式字串設定版本。

在此範例中,StaticVersionStrategy 用於將 v1 後綴附加到任何資源路徑

1
2
3
4
5
6
7
8
9
10
11
12
use Symfony\Component\Asset\Package;
use Symfony\Component\Asset\VersionStrategy\StaticVersionStrategy;

$package = new Package(new StaticVersionStrategy('v1'));

// Absolute path
echo $package->getUrl('/image.png');
// result: /image.png?v1

// Relative path
echo $package->getUrl('image.png');
// result: image.png?v1

如果您想修改版本格式,請將相容於 sprintf 的格式字串作為 StaticVersionStrategy 建構子的第二個引數傳遞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// puts the 'version' word before the version value
$package = new Package(new StaticVersionStrategy('v1', '%s?version=%s'));

echo $package->getUrl('/image.png');
// result: /image.png?version=v1

// puts the asset version before its path
$package = new Package(new StaticVersionStrategy('v1', '%2$s/%1$s'));

echo $package->getUrl('/image.png');
// result: /v1/image.png

echo $package->getUrl('image.png');
// result: v1/image.png

JSON 檔案清單

一種用於管理資源版本控制的熱門策略(Webpack 等工具使用)是產生一個 JSON 檔案,將所有來源檔案名稱對應到其對應的輸出檔案

1
2
3
4
5
{
    "css/app.css": "build/css/app.b916426ea1d10021f3f17ce8031f93c2.css",
    "js/app.js": "build/js/app.13630905267b809161e71d0f8a0c017b.js",
    "...": "..."
}

在這些情況下,請使用 JsonManifestVersionStrategy

1
2
3
4
5
6
7
8
use Symfony\Component\Asset\Package;
use Symfony\Component\Asset\VersionStrategy\JsonManifestVersionStrategy;

// assumes the JSON file above is called "rev-manifest.json"
$package = new Package(new JsonManifestVersionStrategy(__DIR__.'/rev-manifest.json'));

echo $package->getUrl('css/app.css');
// result: build/css/app.b916426ea1d10021f3f17ce8031f93c2.css

如果您請求的資源在 rev-manifest.json 檔案中找不到,則會傳回原始的(未修改的)資源路徑。$strictMode 引數有助於偵錯問題,因為當資源未在清單中列出時,它會擲回例外狀況

1
2
3
4
5
6
7
8
9
10
use Symfony\Component\Asset\Package;
use Symfony\Component\Asset\VersionStrategy\JsonManifestVersionStrategy;

// The value of $strictMode can be specific per environment "true" for debugging and "false" for stability.
$strictMode = true;
// assumes the JSON file above is called "rev-manifest.json"
$package = new Package(new JsonManifestVersionStrategy(__DIR__.'/rev-manifest.json', null, $strictMode));

echo $package->getUrl('not-found.css');
// error:

如果您的 JSON 檔案不在本機檔案系統上,但可透過 HTTP 存取,請將 JsonManifestVersionStrategy 與 HttpClient 組件搭配使用

1
2
3
4
5
6
7
use Symfony\Component\Asset\Package;
use Symfony\Component\Asset\VersionStrategy\JsonManifestVersionStrategy;
use Symfony\Component\HttpClient\HttpClient;

$httpClient = HttpClient::create();
$manifestUrl = 'https://cdn.example.com/rev-manifest.json';
$package = new Package(new JsonManifestVersionStrategy($manifestUrl, $httpClient));

自訂版本控制策略

使用 VersionStrategyInterface 來定義您自己的版本控制策略。例如,您的應用程式可能需要將目前日期附加到其所有網頁資源,以便每天清除快取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use Symfony\Component\Asset\VersionStrategy\VersionStrategyInterface;

class DateVersionStrategy implements VersionStrategyInterface
{
    private string $version;

    public function __construct()
    {
        $this->version = date('Ymd');
    }

    public function getVersion(string $path): string
    {
        return $this->version;
    }

    public function applyVersion(string $path): string
    {
        return sprintf('%s?v=%s', $path, $this->getVersion($path));
    }
}

群組化 Asset

通常,許多資源都位於一個通用路徑下(例如 /static/images)。如果您的情況是這樣,請將預設的 Package 類別替換為 PathPackage,以避免一遍又一遍地重複該路徑

1
2
3
4
5
6
7
8
9
10
11
use Symfony\Component\Asset\PathPackage;
// ...

$pathPackage = new PathPackage('/static/images', new StaticVersionStrategy('v1'));

echo $pathPackage->getUrl('logo.png');
// result: /static/images/logo.png?v1

// Base path is ignored when using absolute paths
echo $pathPackage->getUrl('/logo.png');
// result: /logo.png?v1

請求上下文感知資源

如果您也在專案中使用 HttpFoundation 組件(例如在 Symfony 應用程式中),PathPackage 類別可以將目前請求的上下文納入考量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use Symfony\Component\Asset\Context\RequestStackContext;
use Symfony\Component\Asset\PathPackage;
// ...

$pathPackage = new PathPackage(
    '/static/images',
    new StaticVersionStrategy('v1'),
    new RequestStackContext($requestStack)
);

echo $pathPackage->getUrl('logo.png');
// result: /somewhere/static/images/logo.png?v1

// Both "base path" and "base url" are ignored when using absolute path for asset
echo $pathPackage->getUrl('/logo.png');
// result: /logo.png?v1

現在請求上下文已設定,PathPackage 將前置目前請求的基礎 URL。因此,例如,如果您的整個網站託管在網路伺服器根目錄的 /somewhere 目錄下,且設定的基礎路徑為 /static/images,則所有路徑都會加上 /somewhere/static/images 前綴。

絕對路徑 Asset 和 CDN

在不同網域和 CDN(內容傳遞網路)上託管資源的應用程式應使用 UrlPackage 類別來為其資源產生絕對 URL

1
2
3
4
5
6
7
8
9
10
use Symfony\Component\Asset\UrlPackage;
// ...

$urlPackage = new UrlPackage(
    'https://static.example.com/images/',
    new StaticVersionStrategy('v1')
);

echo $urlPackage->getUrl('/logo.png');
// result: https://static.example.com/images/logo.png?v1

您也可以傳遞與協定無關的 URL

1
2
3
4
5
6
7
8
9
10
use Symfony\Component\Asset\UrlPackage;
// ...

$urlPackage = new UrlPackage(
    '//static.example.com/images/',
    new StaticVersionStrategy('v1')
);

echo $urlPackage->getUrl('/logo.png');
// result: //static.example.com/images/logo.png?v1

這很有用,因為如果訪客在 https 中檢視您的網站,資源將自動透過 HTTPS 請求。如果您想使用此功能,請確保您的 CDN 主機支援 HTTPS。

如果您從多個網域提供資源以提高應用程式效能,請將 URL 陣列作為 UrlPackage 建構子的第一個引數傳遞

1
2
3
4
5
6
7
8
9
10
11
12
13
use Symfony\Component\Asset\UrlPackage;
// ...

$urls = [
    'https://static1.example.com/images/',
    'https://static2.example.com/images/',
];
$urlPackage = new UrlPackage($urls, new StaticVersionStrategy('v1'));

echo $urlPackage->getUrl('/logo.png');
// result: https://static1.example.com/images/logo.png?v1
echo $urlPackage->getUrl('/icon.png');
// result: https://static2.example.com/images/icon.png?v1

對於每個資源,將隨機使用其中一個 URL。但是,選擇是確定性的,這表示每個資源將始終由同一個網域提供服務。這種行為簡化了 HTTP 快取的管理。

請求上下文感知資源

與應用程式相對路徑資源類似,絕對路徑資源也可以將目前請求的上下文納入考量。在這種情況下,僅考慮請求協定,以便選擇適當的基礎 URL(HTTPS 或協定相對 URL 用於 HTTPS 請求,任何基礎 URL 用於 HTTP 請求)

1
2
3
4
5
6
7
8
9
10
11
12
13
use Symfony\Component\Asset\Context\RequestStackContext;
use Symfony\Component\Asset\UrlPackage;
// ...

$urlPackage = new UrlPackage(
    ['http://example.com/', 'https://example.com/'],
    new StaticVersionStrategy('v1'),
    new RequestStackContext($requestStack)
);

echo $urlPackage->getUrl('/logo.png');
// assuming the RequestStackContext says that we are on a secure host
// result: https://example.com/logo.png?v1

具名套件

管理大量不同資源的應用程式可能需要將它們分組到具有相同版本控制策略和基礎路徑的套件中。Asset 組件包含一個 Packages 類別,以簡化多個套件的管理。

在以下範例中,所有套件都使用相同的版本控制策略,但它們都具有不同的基礎路徑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use Symfony\Component\Asset\Package;
use Symfony\Component\Asset\Packages;
use Symfony\Component\Asset\PathPackage;
use Symfony\Component\Asset\UrlPackage;
// ...

$versionStrategy = new StaticVersionStrategy('v1');

$defaultPackage = new Package($versionStrategy);

$namedPackages = [
    'img' => new UrlPackage('https://img.example.com/', $versionStrategy),
    'doc' => new PathPackage('/somewhere/deep/for/documents', $versionStrategy),
];

$packages = new Packages($defaultPackage, $namedPackages);

Packages 類別允許定義預設套件,該套件將套用於未定義要使用之套件名稱的資源。此外,此應用程式定義了一個名為 img 的套件,用於從外部網域提供圖像,以及一個 doc 套件,以避免在連結到範本內的文件時重複長路徑

1
2
3
4
5
6
7
8
echo $packages->getUrl('/main.css');
// result: /main.css?v1

echo $packages->getUrl('/logo.png', 'img');
// result: https://img.example.com/logo.png?v1

echo $packages->getUrl('resume.pdf', 'doc');
// result: /somewhere/deep/for/documents/resume.pdf?v1

本機檔案和其他協定

除了 HTTP 之外,此組件還支援其他協定(例如 file:// 和 ftp://)。這允許例如提供本機檔案以提高效能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use Symfony\Component\Asset\UrlPackage;
// ...

$localPackage = new UrlPackage(
    'file:///path/to/images/',
    new EmptyVersionStrategy()
);

$ftpPackage = new UrlPackage(
    'ftp://example.com/images/',
    new EmptyVersionStrategy()
);

echo $localPackage->getUrl('/logo.png');
// result: file:///path/to/images/logo.png

echo $ftpPackage->getUrl('/logo.png');
// result: ftp://example.com/images/logo.png

了解更多

本作品(包括程式碼範例)根據創用 CC BY-SA 3.0 授權條款授權。
目錄
    版本