Компонент Asset

Дата оновлення перекладу 2024-04-30

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

Ця практика більше не рекомендується окрім випадків, коли веб-додаток дуже простий. Жорстке кодування може бути недоліком, тому що:

  • Шаблони стають багатослівними: вам потрібно написати повний шлях для кожного ресурсу. При використанні компонента Asset, ви можете згрупувати ресурси в пакети, щоб уникнути повторення спільної частини їхнього шляху;
  • Ускладнюється версіонування: воно має бути оброблено вручну для кожного додатку. Додавання версії (наприклад, main.css?v=5) до URL ресурсів важливо для деяких додатків, так як дозволяє вам визначати різні стратегії версіонування для кожного пакету;
  • Переміщення локації ресурсів стає складним та ненадійним: воно вимагає обережного оновлення URL всіх ресурсів, доданих в усі шаблони. Компонент Asset дозволяє переміщувати ресурси без зусиль, просто змінюючи базове значення шляху, повʼязане з пакетом ресурсів;
  • Майже неможливо використовувати декілька CDN: ця техніка вимагає від вас зміни URL ресурсу рандомно для кожного запиту. Компонент Asset надає готову до використання підтримку для будь-якої кількості 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 для ресурсів

Версіоновані ресурси

Однією з основних функцій компонента 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'));

// Абсолютний шлях
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
{
    "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 допомагає налагоджувати проблеми, оскільки він викликає виключення, якщо ресурс відсутній у маніфесті:

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

// Значення $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 \DateTimeInterface $version;

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

    public function getVersion(string $path): \DateTimeInterface
    {
        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');
// result: /somewhere/static/images/logo.png?v1

// Як "base path", так і "base url" ігнорується при використанні абсолютного шляху для ресурсу
echo $pathPackage->getUrl('/logo.png');
// result: /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

Дізнайтеся більше