Nette Assets

Masz dość ręcznego zarządzania plikami statycznymi w swoich aplikacjach webowych? Zapomnij o kodowaniu ścieżek na stałe, problemach z unieważnianiem pamięci podręcznej czy martwieniu się o wersjonowanie plików. Nette Assets zmienia sposób, w jaki pracujesz z obrazami, arkuszami stylów, skryptami i innymi zasobami statycznymi.

  • Inteligentne wersjonowanie zapewnia, że przeglądarki zawsze ładują najnowsze pliki
  • Automatyczne wykrywanie typów plików i wymiarów
  • Bezproblemowa integracja z Latte dzięki intuicyjnym tagom
  • Elastyczna architektura wspierająca systemy plików, CDN i Vite
  • Leniwe ładowanie dla optymalnej wydajności

Dlaczego Nette Assets?

Praca z plikami statycznymi często oznacza powtarzalny, podatny na błędy kod. Ręcznie konstruujesz adresy URL, dodajesz parametry wersji dla unieważniania pamięci podręcznej i obsługujesz różne typy plików w różny sposób. Prowadzi to do kodu takiego jak:

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

Z Nette Assets cała ta złożoność znika:

{* Wszystko zautomatyzowane - URL, wersjonowanie, wymiary *}
<img n:asset="images/logo.png">
<link n:asset="css/style.css">

{* Albo po prostu *}
{asset 'css/style.css'}

To wszystko! Biblioteka automatycznie:

  • Dodaje parametry wersji na podstawie czasu modyfikacji pliku
  • Wykrywa wymiary obrazu i uwzględnia je w HTML
  • Generuje prawidłowy element HTML dla każdego typu pliku
  • Obsługuje zarówno środowiska deweloperskie, jak i produkcyjne

Instalacja

Zainstaluj Nette Assets za pomocą Composera:

composer require nette/assets

Wymaga PHP 8.1 lub nowszego i działa idealnie z Nette Framework, ale może być również używany samodzielnie.

Pierwsze kroki

Nette Assets działa od razu po wyjęciu z pudełka bez żadnej konfiguracji. Umieść swoje pliki statyczne w katalogu www/assets/ i zacznij ich używać:

{* Wyświetl obraz z automatycznymi wymiarami *}
{asset 'logo.png'}

{* Dołącz arkusz stylów z wersjonowaniem *}
{asset 'style.css'}

{* Załaduj moduł JavaScript *}
{asset 'app.js'}

Aby mieć większą kontrolę nad generowanym HTML, użyj atrybutu n:asset lub funkcji asset().

Jak to działa

Nette Assets opiera się na trzech kluczowych koncepcjach, które sprawiają, że jest potężny, a jednocześnie prosty w użyciu:

Zasoby – Twoje pliki stają się inteligentne

Zasób reprezentuje dowolny plik statyczny w Twojej aplikacji. Każdy plik staje się obiektem z przydatnymi właściwościami tylko do odczytu:

$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'

Różne typy plików udostępniają różne właściwości:

  • Obrazy: szerokość, wysokość, tekst alternatywny, leniwe ładowanie
  • Skrypty: typ modułu, hashe integralności, crossorigin
  • Arkusze stylów: zapytania mediów, integralność
  • Audio/Wideo: czas trwania, wymiary
  • Czcionki: prawidłowe wstępne ładowanie z CORS

Biblioteka automatycznie wykrywa typy plików i tworzy odpowiednią klasę zasobów.

Mappery – Skąd pochodzą pliki

Mapper wie, jak znaleźć pliki i utworzyć dla nich adresy URL. Możesz mieć wiele mapperów do różnych celów – plików lokalnych, CDN, przechowywania w chmurze lub narzędzi do budowania (każdy z nich ma nazwę). Wbudowany FilesystemMapper obsługuje pliki lokalne, natomiast ViteMapper integruje się z nowoczesnymi narzędziami do budowania.

Mappery są definiowane w konfiguracji.

Rejestr – Twój główny interfejs

Rejestr zarządza wszystkimi mapperami i udostępnia główne API:

// Wstrzyknij rejestr do swojej usługi
public function __construct(
	private Nette\Assets\Registry $assets
) {}

// Pobierz zasoby z różnych mapperów
$logo = $this->assets->getAsset('images:logo.png'); // mapper 'image'
$app = $this->assets->getAsset('app:main.js'); // mapper 'app'
$style = $this->assets->getAsset('style.css'); // używa domyślnego mappera

Rejestr automatycznie wybiera odpowiedni mapper i buforuje wyniki dla zwiększenia wydajności.

Praca z zasobami w PHP

Rejestr udostępnia dwie metody pobierania zasobów:

// Rzuca Nette\Assets\AssetNotFoundException, jeśli plik nie istnieje
$logo = $assets->getAsset('logo.png');

// Zwraca null, jeśli plik nie istnieje
$banner = $assets->tryGetAsset('banner.jpg');
if ($banner) {
	echo $banner->url;
}

Określanie mapperów

Możesz jawnie wybrać, którego mappera użyć:

// Użyj domyślnego mappera
$file = $assets->getAsset('document.pdf');

// Użyj konkretnego mappera z prefiksem
$image = $assets->getAsset('images:photo.jpg');

// Użyj konkretnego mappera z składnią tablicową
$script = $assets->getAsset(['scripts', 'app.js']);

Właściwości i typy zasobów

Każdy typ zasobu udostępnia odpowiednie właściwości tylko do odczytu:

// Właściwości obrazu
$image = $assets->getAsset('photo.jpg');
echo $image->width;     // 1920
echo $image->height;    // 1080
echo $image->mimeType;  // 'image/jpeg'

// Właściwości skryptu
$script = $assets->getAsset('app.js');
echo $script->type;     // 'module' or null

// Właściwości audio
$audio = $assets->getAsset('song.mp3');
echo $audio->duration;  // duration in seconds

// Wszystkie zasoby mogą być rzutowane na ciąg znaków (zwraca URL)
$url = (string) $assets->getAsset('document.pdf');

Właściwości takie jak wymiary czy czas trwania są ładowane leniwie tylko przy dostępie, co utrzymuje bibliotekę szybką.

Używanie zasobów w szablonach Latte

Nette Assets zapewnia intuicyjną integrację Latte z tagami i funkcjami.

{asset}

Tag {asset} renderuje kompletne elementy HTML:

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

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

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

Tag automatycznie:

  • Wykrywa typ zasobu i generuje odpowiedni HTML
  • Włącza wersjonowanie dla unieważniania pamięci podręcznej
  • Dodaje wymiary dla obrazów
  • Ustawia prawidłowe atrybuty (typ, media itp.)

Użyty wewnątrz atrybutów HTML, wyprowadza tylko URL:

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

n:asset

Dla pełnej kontroli nad atrybutami HTML:

{* Atrybut n:asset wypełnia src, wymiary itp. *}
<img n:asset="product.jpg" alt="Product" class="rounded">

{* Działa z każdym odpowiednim elementem *}
<script n:asset="analytics.js" defer></script>
<link n:asset="print.css" media="print">
<audio n:asset="podcast.mp3" controls></audio>

Użyj zmiennych i mapperów:

{* Zmienne działają naturalnie *}
<img n:asset="$product->image">

{* Określ mapper za pomocą nawiasów klamrowych *}
<img n:asset="images:{$product->image}">

{* Określ mapper za pomocą notacji tablicowej *}
<img n:asset="[images, $product->image]">

asset()

Dla maksymalnej elastyczności użyj funkcji asset():

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

{* Albo bezpośrednio *}
<img src={asset('logo.png')} alt="Logo">

Opcjonalne zasoby

Obsługuj brakujące zasoby elegancko za pomocą {asset?}, n:asset? i tryAsset():

{* Opcjonalny tag - nie renderuje niczego, jeśli zasób brakuje *}
{asset? 'optional-banner.jpg'}

{* Opcjonalny atrybut - pomija, jeśli zasób brakuje *}
<img n:asset?="user-avatar.jpg" alt="Avatar" class="avatar">

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

{preload}

Popraw wydajność ładowania strony:

{* W sekcji <head> *}
{preload 'critical.css'}
{preload 'important-font.woff2'}
{preload 'hero-image.jpg'}

Generuje odpowiednie linki preload:

<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">

Zaawansowane funkcje

Automatyczne wykrywanie rozszerzeń

Automatycznie obsługuj wiele formatów:

assets:
	mapping:
		images:
			path: img
			extension: [webp, jpg, png]  # Spróbuj w kolejności

Teraz możesz żądać bez rozszerzenia:

{* Automatycznie znajduje logo.webp, logo.jpg lub logo.png *}
{asset 'images:logo'}

Idealne do progresywnego ulepszania z nowoczesnymi formatami.

Inteligentne wersjonowanie

Pliki są automatycznie wersjonowane na podstawie czasu modyfikacji:

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

Po zaktualizowaniu pliku, znacznik czasu zmienia się, wymuszając odświeżenie pamięci podręcznej przeglądarki.

Kontroluj wersjonowanie dla każdego zasobu:

// Wyłącz wersjonowanie dla konkretnego zasobu
$asset = $assets->getAsset('style.css', ['version' => false]);

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

Zasoby czcionek

Czcionki są traktowane specjalnie z prawidłowym CORS:

{* Prawidłowe wstępne ładowanie z crossorigin *}
{preload 'fonts:OpenSans-Regular.woff2'}

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

Niestandardowe mappery

Twórz niestandardowe mappery dla specjalnych potrzeb, takich jak przechowywanie w chmurze lub dynamiczne generowanie:

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);
	}
}

Zarejestruj w konfiguracji:

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

Użyj jak każdego innego mappera:

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

Metoda Helpers::createAssetFromUrl() automatycznie tworzy prawidłowy typ zasobu na podstawie rozszerzenia pliku.

Więcej informacji

wersja: 1.0