Компонент Asset
Дата обновления перевода 2024-07-03
Компонент Asset
Компонент Asset управляет генерированием URL и версионированием веб-ресурсов вроде таблиц стилей CSS, файлов JavaScript и изображений.
В прошлом, веб-приложения зачастую жёстко кодировали URL веб-ресурсов. Например:
1 2 3 4 5
<link rel="stylesheet" type="text/css" href="/css/main.css">
<!-- ... -->
<a href="/"><img src="/images/logo.png"></a>
Эта практика больше не рекомендуется кроме случаев, когда веб-приложение очень простое. Жёсткое кодирование URL может быть недостатком потому что:
- Шаблоны становятся многословыми: вам нужно написать полный путь для каждого ресурса. При использовании компонента Ресурс, вы можете сгруппировать ресурсы в пакеты, чтобы избежать повторения общей части их пути;
- Усложняется версионирование: оно должно быть обработано вручную для каждого
приложения. Добавление версии (например,
main.css?v=5
) к URL ресурсов важно для некоторых приложений, так как позволяет вам контролирвать кеширование ресурсов. Компонент Ресурс позволяет вам определять разные стратегии версионирования для каждого пакета; - Перемещение локации ресурсов становится напряжным и ненадёжным: оно требует осторожного обновления URL всех ресурсов, включённых во все шаблоны. Компонент Ресурс позволяет перемещать ресурсы без усилий, просто изменяя базовое значение пути, связанное с пакетом ресурсов;
- Почти невозможно использовать несколько CDN: эта техника требует от вас
изменения URL ресурса рандомно для каждого запроса. Компонент Ресурс предоставляет
готовую к использованию поддержку для любого количества CDN, как обычных (
http://
), так и безопасных (https://
).
Установка
1
$ composer require symfony/asset
Note
Если вы устанавливаете этот компонент вне приложения Symfony, вам нужно
подключить файл vendor/autoload.php
в вашем коде для включения механизма
автозагрузки классов, предоставляемых Composer. Детальнее читайте в
этой статье.
Использование
Пакеты ресурсов
Компонент 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());
// Абсолютный путь
echo $package->getUrl('/image.png');
// результат: /image.png
// Относительный путь
echo $package->getUrl('image.png');
// результат: image.png
Пакеты реализуют PackageInterface, которй определяет следующие два метода:
- getVersion()
- Возвращает версию ресурса для ресурса.
- getUrl()
- Возвращает абсолютный или публичный релятивный корневой путь.
С пакетом вы можете:
A) версионировать ресурсы ;
B) устанавливать общий базовый путь (например, /css
)
для пакетов;
C) конфигурировать CDN for the assets
Версионрованные ресурсы
Одной из основных функций компонента Ресурс является возможность усправлять версионированием ресурсов приложения. Версии ресурсов зачастую используются для контролирования того, как эти ресурсы кешируются.
Вместо того, чтобы полагаться на простой механизм версии, компонент Ресурс позволяет вам определять продвинутые стратегии версионирования через 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'));
// Абсолютный путь
echo $package->getUrl('/image.png');
// результат: /image.png?v1
// Относительный путь:
echo $package->getUrl('image.png');
// результат: image.png?v1
В случае, если вы хотите измеить формат версии, передайте строку формата,
совместимого с sprintf
в качестве второго аргумента конструктора StaticVersionStrategy
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// помещает слово 'version' перед значением версии
$package = new Package(new StaticVersionStrategy('v1', '%s?version=%s'));
echo $package->getUrl('/image.png');
// результат: /image.png?version=v1
// помещает версию ресурса перед его путём
$package = new Package(new StaticVersionStrategy('v1', '%2$s/%1$s'));
echo $package->getUrl('/image.png');
// результат: /v1/image.png
echo $package->getUrl('image.png');
// результат: v1/image.png
JSON файл-манифест
Популярный метод управления версиями ресурсов, используемым во многих инструментах, таких как Webpack, это создание JSON-файла с указанием связей всех исходных файлов и соответствующих результирующих файлов:
1 2 3 4 5 6
// rev-manifest.json
{
"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;
// предполагает, что файл JSON выше называется "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
помогает отлаживать проблемы, поскольку вызывает исключение, если ресурс не указан
в манифесте:
use SymfonyComponentAssetPackage; use SymfonyComponentAssetVersionStrategyJsonManifestVersionStrategy;
// Значение $strictMode может быть особым для окружения - "true" для отладки и "false" для стабильности. $strictMode = true; // предполагает, что файл JSON выше называется "rev-manifest.json" $package = new Package(new JsonManifestVersionStrategy(__DIR__.'/rev-manifest.json', null, $strictMode));
echo $package->getUrl('not-found.css'); // error:
Если ваш JSON-файл находится не в вашей локальной файловой системе, а доступен по протоколу HTTP, используйте RemoteJsonManifestVersionStrategy. с компонентом HttpClient:
1 2 3 4 5 6 7
use Symfony\Component\Asset\Package;
use Symfony\Component\Asset\VersionStrategy\RemoteJsonManifestVersionStrategy;
use Symfony\Component\HttpClient\HttpClient;
$httpClient = HttpClient::create();
$manifestUrl = 'https://cdn.example.com/rev-manifest.json';
$package = new Package(new RemoteJsonManifestVersionStrategy($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));
}
}
Сгруппированные ресурсы
Зачастую многие ресурсы живут по общему пути (например, /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');
// результат: /static/images/logo.png?v1
// Базовый путь игнорируется при использовании абсолютных путей
echo $pathPackage->getUrl('/logo.png');
// результат: /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');
// результат: /somewhere/static/images/logo.png?v1
// Как "base path", так и "base url" игнорируются при использовании абсолютного пути для ресурса
echo $pathPackage->getUrl('/logo.png');
// результат: /logo.png?v1
Теперь, когда контекст запроса установлен, PathPackage
добавит текущий запрос
к базовому URL. Так что, например, если весь ваш сайт находится в каталоге /somewhere
вашего корневого каталога веб-сервера, а сконфигурированный базовый путь - /static/images
,
то все пути будут иметь префикс /somewhere/static/images
.
Абсолютные ресурсы и 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 $package->getUrl('/logo.png');
// предполагая, что RequestStackContext сообщает, что мы на защищённом хосте
// результат: https://example.com/logo.png?v1
Именованные пакеты
Приложения, которые управляют множеством разных ресурсов, могут захотеть сгруппировать их в пакеты с одинаковой стратегией версионирования и базовым путём. Компонент Ресурс включает в себя класс 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
Узнайте больше
- Как управлять ресурсами CSS и JavaScript в приложениях Symfony
- Компонент WebLink для предварительной загрузки ресурсов с использованием HTTP/2.