Nette Assets

Устали вручную управлять статическими файлами в ваших веб-приложениях? Забудьте о жестком кодировании путей, проблемах с инвалидацией кэша или беспокойстве о версионировании файлов. Nette Assets преобразует ваш способ работы с изображениями, таблицами стилей, скриптами и другими статическими ресурсами.

  • Умное версионирование гарантирует, что браузеры всегда загружают последние версии файлов
  • Автоматическое определение типов и размеров файлов
  • Бесшовная интеграция с Latte с интуитивно понятными тегами
  • Гибкая архитектура, поддерживающая файловые системы, CDN и Vite
  • Ленивая загрузка для оптимальной производительности

Зачем Nette Assets?

Работа со статическими файлами часто означает повторяющийся, подверженный ошибкам код. Вы вручную конструируете URL, добавляете параметры версии для обхода кэша и по-разному обрабатываете различные типы файлов. Это приводит к такому коду:

<img src="/images/logo.png?v=1699123456" width="200" height="100" alt="Logo">
<link rel="stylesheet" href="/css/style.css?v=2">

С Nette Assets вся эта сложность исчезает:

{* Everything automated - URL, versioning, dimensions *}
<img n:asset="images/logo.png">
<link n:asset="css/style.css">

{* Or just *}
{asset 'css/style.css'}

Вот и все! Библиотека автоматически:

  • Добавляет параметры версии на основе времени модификации файла
  • Определяет размеры изображения и включает их в HTML
  • Генерирует правильный HTML-элемент для каждого типа файла
  • Обрабатывает как среды разработки, так и производственные среды

Установка

Установите Nette Assets с помощью Composer:

composer require nette/assets

Требуется PHP 8.1 или выше, и он отлично работает с Nette Framework, но также может использоваться автономно.

Первые шаги

Nette Assets работает из коробки без какой-либо конфигурации. Разместите свои статические файлы в каталоге www/assets/ и начните их использовать:

{* Display an image with automatic dimensions *}
{asset 'logo.png'}

{* Include a stylesheet with versioning *}
{asset 'style.css'}

{* Load a JavaScript module *}
{asset 'app.js'}

Для большего контроля над генерируемым HTML используйте атрибут n:asset или функцию asset().

Как это работает

Nette Assets построен на трех основных концепциях, которые делают его мощным, но простым в использовании:

Активы – Ваши файлы стали умнее

Актив представляет собой любой статический файл в вашем приложении. Каждый файл становится объектом с полезными свойствами только для чтения:

$image = $assets->getAsset('photo.jpg');
echo $image->url;      // '/assets/photo.jpg?v=1699123456'
echo $image->width;    // 1920
echo $image->height;   // 1080
echo $image->mimeType; // 'image/jpeg'

Различные типы файлов предоставляют различные свойства:

  • Изображения: ширина, высота, альтернативный текст, ленивая загрузка
  • Скрипты: тип модуля, хеши целостности, crossorigin
  • Таблицы стилей: медиа-запросы, целостность
  • Аудио/Видео: продолжительность, размеры
  • Шрифты: правильная предварительная загрузка с CORS

Библиотека автоматически определяет типы файлов и создает соответствующий класс актива.

Сопоставители – Откуда берутся файлы

Сопоставитель знает, как находить файлы и создавать для них URL. У вас может быть несколько сопоставителей для разных целей – локальные файлы, CDN, облачное хранилище или инструменты сборки (каждый из них имеет имя). Встроенный FilesystemMapper обрабатывает локальные файлы, а ViteMapper интегрируется с современными инструментами сборки.

Сопоставители определяются в конфигурации.

Реестр – Ваш основной интерфейс

Реестр управляет всеми сопоставителями и предоставляет основной API:

// Inject the registry in your service
public function __construct(
	private Nette\Assets\Registry $assets
) {}

// Get assets from different mappers
$logo = $this->assets->getAsset('images:logo.png'); // 'image' mapper
$app = $this->assets->getAsset('app:main.js'); // 'app' mapper
$style = $this->assets->getAsset('style.css'); // uses default mapper

Реестр автоматически выбирает правильный сопоставитель и кэширует результаты для повышения производительности.

Работа с активами в PHP

Реестр предоставляет два метода для получения активов:

// Throws Nette\Assets\AssetNotFoundException if file doesn't exist
$logo = $assets->getAsset('logo.png');

// Returns null if file doesn't exist
$banner = $assets->tryGetAsset('banner.jpg');
if ($banner) {
	echo $banner->url;
}

Указание сопоставителей

Вы можете явно выбрать, какой сопоставитель использовать:

// Use default mapper
$file = $assets->getAsset('document.pdf');

// Use specific mapper with prefix
$image = $assets->getAsset('images:photo.jpg');

// Use specific mapper with array syntax
$script = $assets->getAsset(['scripts', 'app.js']);

Свойства и типы активов

Каждый тип актива предоставляет соответствующие свойства только для чтения:

// Image properties
$image = $assets->getAsset('photo.jpg');
echo $image->width;     // 1920
echo $image->height;    // 1080
echo $image->mimeType;  // 'image/jpeg'

// Script properties
$script = $assets->getAsset('app.js');
echo $script->type;     // 'module' or null

// Audio properties
$audio = $assets->getAsset('song.mp3');
echo $audio->duration;  // duration in seconds

// All assets can be cast to string (returns URL)
$url = (string) $assets->getAsset('document.pdf');

Свойства, такие как размеры или продолжительность, загружаются лениво только при обращении к ним, что обеспечивает быструю работу библиотеки.

Использование активов в Latte-шаблонах

Nette Assets обеспечивает интуитивно понятную интеграцию с Latte с помощью тегов и функций.

{asset}

Тег {asset} отображает полные HTML-элементы:

{* Renders: <img src="/assets/hero.jpg?v=123" width="1920" height="1080"> *}
{asset 'hero.jpg'}

{* Renders: <script src="/assets/app.js?v=456" type="module"></script> *}
{asset 'app.js'}

{* Renders: <link rel="stylesheet" href="/assets/style.css?v=789"> *}
{asset 'style.css'}

Тег автоматически:

  • Определяет тип актива и генерирует соответствующий HTML
  • Включает версионирование для обхода кэша
  • Добавляет размеры для изображений
  • Устанавливает правильные атрибуты (type, media и т.д.)

При использовании внутри HTML-атрибутов он выводит только URL:

<div style="background-image: url({asset 'bg.jpg'})">
<img srcset="{asset 'logo@2x.png'} 2x">

n:asset

Для полного контроля над HTML-атрибутами:

{* The n:asset attribute fills in src, dimensions, etc. *}
<img n:asset="product.jpg" alt="Product" class="rounded">

{* Works with any relevant element *}
<script n:asset="analytics.js" defer></script>
<link n:asset="print.css" media="print">
<audio n:asset="podcast.mp3" controls></audio>

Используйте переменные и сопоставители:

{* Variables work naturally *}
<img n:asset="$product->image">

{* Specify mapper with curly brackets *}
<img n:asset="images:{$product->image}">

{* Specify mapper with array notation *}
<img n:asset="[images, $product->image]">

asset()

Для максимальной гибкости используйте функцию asset():

{var $logo = asset('logo.png')}
<img src={$logo} width={$logo->width} height={$logo->height}>

{* Or directly *}
<img src={asset('logo.png')} alt="Logo">

Опциональные активы

Избегайте ошибок при отсутствии активов с помощью {asset?}, n:asset? и tryAsset():

{* Optional tag - renders nothing if asset missing *}
{asset? 'optional-banner.jpg'}

{* Optional attribute - skips if asset missing *}
<img n:asset?="user-avatar.jpg" alt="Avatar" class="avatar">

{* With fallback *}
{var $avatar = tryAsset('user-avatar.jpg') ?? asset('default-avatar.jpg')}
<img n:asset=$avatar alt="Avatar">

{preload}

Улучшите производительность загрузки страницы:

{* In your <head> section *}
{preload 'critical.css'}
{preload 'important-font.woff2'}
{preload 'hero-image.jpg'}

Генерирует соответствующие ссылки предварительной загрузки:

<link rel="preload" href="/assets/critical.css?v=123" as="style">
<link rel="preload" href="/assets/important-font.woff2" as="font" crossorigin>
<link rel="preload" href="/assets/hero-image.jpg" as="image">

Расширенные возможности

Автоматическое определение расширения

Автоматическая обработка нескольких форматов:

assets:
	mapping:
		images:
			path: img
			extension: [webp, jpg, png]  # Try in order

Теперь вы можете запрашивать без расширения:

{* Finds logo.webp, logo.jpg, or logo.png automatically *}
{asset 'images:logo'}

Идеально подходит для прогрессивного улучшения с помощью современных форматов.

Умное версионирование

Файлы автоматически версионируются на основе времени модификации:

{asset 'style.css'}
{* Output: <link rel="stylesheet" href="/assets/style.css?v=1699123456"> *}

При обновлении файла метка времени изменяется, что принудительно обновляет кэш браузера.

Управление версионированием для каждого актива:

// Disable versioning for specific asset
$asset = $assets->getAsset('style.css', ['version' => false]);

// In Latte
{asset 'style.css', version: false}

Активы шрифтов

Шрифты получают специальную обработку с правильным CORS:

{* Proper preload with crossorigin *}
{preload 'fonts:OpenSans-Regular.woff2'}

{* Use in CSS *}
<style>
@font-face {
	font-family: 'Open Sans';
	src: url('{asset 'fonts:OpenSans-Regular.woff2'}') format('woff2');
	font-display: swap;
}
</style>

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

Создавайте пользовательские сопоставители для особых нужд, таких как облачное хранилище или динамическая генерация:

use Nette\Assets\Mapper;
use Nette\Assets\Asset;
use Nette\Assets\Helpers;

class CloudStorageMapper implements Mapper
{
	public function __construct(
		private CloudClient $client,
		private string $bucket,
	) {}

	public function getAsset(string $reference, array $options = []): Asset
	{
		if (!$this->client->exists($this->bucket, $reference)) {
			throw new Nette\Assets\AssetNotFoundException("Asset '$reference' not found");
		}

		$url = $this->client->getPublicUrl($this->bucket, $reference);
		return Helpers::createAssetFromUrl($url);
	}
}

Зарегистрируйте в конфигурации:

assets:
	mapping:
		cloud: CloudStorageMapper(@cloudClient, 'my-bucket')

Используйте как любой другой сопоставитель:

{asset 'cloud:user-uploads/photo.jpg'}

Метод Helpers::createAssetFromUrl() автоматически создает правильный тип актива на основе расширения файла.

Дальнейшее чтение

версия: 1.0