Компонент Asset

Компонент 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

Также вы можете клонировать репозиторий https://github.com/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
use Symfony\Component\Asset\Package;
use Symfony\Component\Asset\VersionStrategy\JsonManifestVersionStrategy;

$package = new Package(new JsonManifestVersionStrategy(__DIR__'./rev-manifest.json'));

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

Пользовательские стратегии версии

Используйте 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 $version;

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

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

    public function applyVersion($path)
    {
        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\PathPackage;
use Symfony\Component\Asset\Context\RequestStackContext;
// ...

$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(
    'http://static.example.com/images/',
    new StaticVersionStrategy('v1')
);

echo $urlPackage->getUrl('/logo.png');
// результат: http://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');
// результат: //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 = array(
    '//static1.example.com/images/',
    '//static2.example.com/images/',
);
$urlPackage = new UrlPackage($urls, new StaticVersionStrategy('v1'));

echo $urlPackage->getUrl('/logo.png');
// результат: http://static1.example.com/images/logo.png?v1
echo $urlPackage->getUrl('/icon.png');
// результат: http://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\UrlPackage;
use Symfony\Component\Asset\Context\RequestStackContext;
// ...

$package = new UrlPackage(
    array('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\PathPackage;
use Symfony\Component\Asset\UrlPackage;
use Symfony\Component\Asset\Packages;
// ...

$versionStrategy = new StaticVersionStrategy('v1');

$defaultPackage = new Package($versionStrategy);

$namedPackages = array(
    'img' => new UrlPackage('http://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');
// результат: /main.css?v1

echo $packages->getUrl('/logo.png', 'img');
// результат: http://img.example.com/logo.png?v1

echo $packages->getUrl('resume.pdf', 'doc');
// результат: /somewhere/deep/for/documents/resume.pdf?v1