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