Nette Assets
Müde davon, statische Dateien in Ihren Webanwendungen von Hand zu verwalten? Vergessen Sie fest verdrahtete Pfade, das Ungültigmachen des Caches oder Sorgen um die Versionierung von Dateien. Nette Assets verändert grundlegend, wie Sie mit Bildern, Stylesheets, Skripten und anderen statischen Ressourcen arbeiten.
- Intelligente Versionierung sorgt dafür, dass Browser immer die neuesten Dateien laden
- Automatische Erkennung von Dateitypen und Abmessungen
- Nahtlose Latte-Integration mit intuitiven Tags
- Flexible Architektur mit Unterstützung für Dateisysteme, CDNs und Vite
- Lazy Loading für optimale Leistung
Warum Nette Assets?
Die Arbeit mit statischen Dateien bedeutet oft sich wiederholenden, fehleranfälligen Code. Sie bauen URLs von Hand zusammen, ergänzen Versionsparameter fürs Cache Busting und behandeln verschiedene Dateitypen unterschiedlich. Das führt zu Code wie diesem:
<img src="/images/logo.png?v=1699123456" width="200" height="100" alt="Logo">
<link rel="stylesheet" href="/css/style.css?v=2">
Mit Nette Assets verschwindet diese ganze Komplexität:
{* Alles automatisch - URL, Versionierung, Abmessungen *}
<img n:asset="images/logo.png">
<link n:asset="css/style.css">
{* Oder einfach *}
{asset 'css/style.css'}
Das war's! Die Bibliothek erledigt automatisch:
- Sie ergänzt Versionsparameter anhand der Änderungszeit der Datei
- Sie erkennt die Abmessungen von Bildern und trägt sie ins HTML ein
- Sie erzeugt für jeden Dateityp das richtige HTML-Element
- Sie behandelt sowohl die Entwicklungs- als auch die Produktionsumgebung
Installation
Installieren Sie Nette Assets mit Composer:
composer require nette/assets
Es benötigt PHP 8.1 oder höher und funktioniert perfekt mit dem Nette Framework, lässt sich aber auch eigenständig verwenden.
Erste Schritte
Nette Assets funktioniert sofort und ohne jede Konfiguration. Legen Sie Ihre statischen Dateien in das Verzeichnis
www/assets/ und verwenden Sie sie:
{* Ein Bild mit automatischen Abmessungen anzeigen *}
{asset 'logo.png'}
{* Ein Stylesheet mit Versionierung einbinden *}
{asset 'style.css'}
{* Ein Skript laden *}
{asset 'app.js'}
Für mehr Kontrolle über das erzeugte HTML verwenden Sie das Attribut n:asset oder die Funktion
asset().
Wie es funktioniert
Nette Assets baut auf drei Kernkonzepten auf, die es mächtig und zugleich einfach zu verwenden machen:
Assets – Ihre Dateien, intelligent gemacht
Ein Asset steht für eine beliebige statische Datei in Ihrer Anwendung. Jede Datei wird zu einem Objekt mit nützlichen Readonly-Properties:
$image = $assets->getAsset('photo.jpg');
echo $image->url; // '/assets/photo.jpg?v=1699123456'
echo $image->file; // '/var/www/assets/photo.jpg' (lokaler Pfad oder null)
echo $image->width; // 1920
echo $image->height; // 1080
echo $image->mimeType; // 'image/jpeg'
Verschiedene Dateitypen bieten verschiedene Properties:
- Bilder: Breite, Höhe, Alternativtext, Lazy Loading
- Skripte: Modultyp, Integrity-Hashes, crossorigin
- Stylesheets: Media Queries, Integrity
- Audio/Video: Dauer, Abmessungen (nur Video)
- Schriften: korrektes Preloading mit CORS
Die Bibliothek erkennt die Dateitypen automatisch und erzeugt die passende Asset-Klasse.
Mapper – woher die Dateien kommen
Ein Mapper weiß, wie man Dateien findet und URLs für sie erzeugt. Sie können mehrere Mapper für verschiedene Zwecke
haben – lokale Dateien, CDN, Cloud-Speicher oder Build-Werkzeuge (jeder von ihnen hat einen Namen). Der eingebaute
FilesystemMapper kümmert sich um lokale Dateien, während ViteMapper moderne Build-Werkzeuge
einbindet.
Mapper werden in der Konfiguration definiert.
Registry – Ihre Hauptschnittstelle
Die Registry verwaltet alle Mapper und stellt die zentrale API bereit:
// Die Registry in Ihren Service injizieren
public function __construct(
private Nette\Assets\Registry $assets
) {}
// Assets aus verschiedenen Mappern holen
$logo = $this->assets->getAsset('images:logo.png'); // Mapper 'images'
$app = $this->assets->getAsset('app:main.js'); // Mapper 'app'
$style = $this->assets->getAsset('style.css'); // verwendet den Standard-Mapper
Die Registry wählt automatisch den richtigen Mapper und cacht die Ergebnisse für die Leistung.
Arbeiten mit Assets in PHP
Die Registry bietet zwei Methoden, um Assets zu holen:
// Wirft Nette\Assets\AssetNotFoundException, wenn die Datei nicht existiert
$logo = $assets->getAsset('logo.png');
// Gibt null zurück, wenn die Datei nicht existiert
$banner = $assets->tryGetAsset('banner.jpg');
if ($banner) {
echo $banner->url;
}
Mapper angeben
Sie können ausdrücklich wählen, welcher Mapper verwendet wird:
// Standard-Mapper verwenden
$file = $assets->getAsset('document.pdf');
// Bestimmten Mapper über ein Präfix verwenden
$image = $assets->getAsset('images:photo.jpg');
// Bestimmten Mapper in Array-Schreibweise verwenden
$script = $assets->getAsset(['scripts', 'app.js']);
Asset-Eigenschaften und -Typen
Jeder Asset-Typ bietet die passenden Readonly-Properties:
// Eigenschaften eines Bildes
$image = $assets->getAsset('photo.jpg');
echo $image->width; // 1920
echo $image->height; // 1080
echo $image->mimeType; // 'image/jpeg'
// Eigenschaften eines Skripts
$script = $assets->getAsset('app.js');
echo $script->type; // null ('module' bei Vite-Einstiegspunkten)
// Eigenschaften von Audio
$audio = $assets->getAsset('song.mp3');
echo $audio->duration; // Dauer in Sekunden
// Alle Assets lassen sich zu einem String casten (gibt die URL zurück)
$url = (string) $assets->getAsset('document.pdf');
Eigenschaften wie Abmessungen oder Dauer werden erst beim Zugriff geladen, wodurch die Bibliothek schnell bleibt.
Für eine genaue statische Analyse installieren Sie die Extension nette/phpstan-rules. PHPStan kennt dann den konkreten Typ jedes
Assets, sodass getAsset('photo.jpg') als ImageAsset verstanden wird und der Zugriff auf
->width keinen Fehler auslöst.
Assets in Latte-Templates verwenden
Nette Assets bietet eine intuitive Integration in Latte mit Tags und Funktionen.
{asset}
Der Tag {asset} rendert vollständige HTML-Elemente:
{* Rendert: <img src="/assets/hero.jpg?v=123" width="1920" height="1080"> *}
{asset 'hero.jpg'}
{* Rendert: <script src="/assets/app.js?v=456"></script> *}
{asset 'app.js'}
{* Rendert: <link rel="stylesheet" href="/assets/style.css?v=789"> *}
{asset 'style.css'}
Der Tag erledigt automatisch:
- Er erkennt den Asset-Typ und erzeugt das passende HTML
- Er ergänzt die Versionierung fürs Cache Busting
- Er ergänzt die Abmessungen von Bildern
- Er setzt die richtigen Attribute (type, media usw.)
Innerhalb von HTML-Attributen und innerhalb der Elemente <style> und <script> gibt er nur
die URL aus:
<div style="background-image: url({asset 'bg.jpg'})">
<img srcset="{asset 'logo@2x.png'} 2x">
n:asset
Für volle Kontrolle über die HTML-Attribute:
{* Das Attribut n:asset füllt src, Abmessungen usw. aus *}
<img n:asset="product.jpg" alt="Product" class="rounded">
{* Funktioniert mit jedem passenden Element *}
<script n:asset="analytics.js" defer></script>
<link n:asset="print.css" media="print">
<audio n:asset="podcast.mp3" controls></audio>
Variablen und Mapper verwenden:
{* Variablen funktionieren ganz natürlich *}
<img n:asset="$product->image">
{* Mapper mit geschweiften Klammern angeben *}
<img n:asset="images:{$product->image}">
{* Mapper in Array-Schreibweise angeben *}
<img n:asset="[images, $product->image]">
n:asset funktioniert auch bei <a>, wo es href ausfüllt, und bei
<link>, wo es einen Preload-Hinweis erzeugt:
<a n:asset="hero.jpg">Bild herunterladen</a>
Beachten Sie, dass die Varianten für <a> und <link> nur bei renderbaren Assets
funktionieren (einem Bild, einem Skript und so weiter), niemals bei einem GenericAsset wie einem PDF.
Bei Bildern genügt es, nur width (oder nur height) zu setzen, die andere Abmessung wird automatisch
so berechnet, dass das Seitenverhältnis erhalten bleibt:
{* height wird aus dem Seitenverhältnis ergänzt *}
<img n:asset="product.jpg" width="200">
asset()
Für maximale Flexibilität verwenden Sie die Funktion asset():
{var $logo = asset('logo.png')}
<img src={$logo} width={$logo->width} height={$logo->height}>
{* Oder direkt *}
<img src={asset('logo.png')} alt="Logo">
Optionale Assets
Fehlende Assets behandeln Sie elegant mit {asset?}, n:asset? und tryAsset():
{* Optionaler Tag - rendert nichts, wenn das Asset fehlt *}
{asset? 'optional-banner.jpg'}
{* Optionales Attribut - wird übersprungen, wenn das Asset fehlt *}
<img n:asset?="user-avatar.jpg" alt="Avatar" class="avatar">
{* Mit Fallback *}
{var $avatar = tryAsset('user-avatar.jpg') ?? asset('default-avatar.jpg')}
<img n:asset=$avatar alt="Avatar">
{preload}
Verbessern Sie die Ladegeschwindigkeit der Seite:
{* In Ihrem Abschnitt <head> *}
{preload 'critical.css'}
{preload 'important-font.woff2'}
{preload 'hero-image.jpg'}
Erzeugt die passenden Preload-Links:
<link rel="preload" href="/assets/critical.css?v=123" as="style">
<link rel="preload" href="/assets/important-font.woff2?v=456" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/assets/hero-image.jpg?v=789" as="image" type="image/jpeg">
Wenn Ihre Response einen Header Content-Security-Policy mit einem nonce setzt, ergänzt
Nette automatisch das passende Attribut nonce bei jedem erzeugten Element <script>,
<link> und <style>, damit sie nicht von der Sicherheitsrichtlinie des Browsers blockiert
werden.
Erweiterte Funktionen
Automatische Erkennung der Endung
Behandeln Sie mehrere Formate automatisch:
assets:
mapping:
images:
path: img
extension: [webp, jpg, png] # der Reihe nach probieren
Nun können Sie ohne Endung anfragen:
{* Findet automatisch logo.webp, logo.jpg oder logo.png *}
{asset 'images:logo'}
Perfekt für Progressive Enhancement mit modernen Formaten.
Intelligente Versionierung
Dateien werden automatisch anhand ihrer Änderungszeit versioniert:
{asset 'style.css'}
{* Ausgabe: <link rel="stylesheet" href="/assets/style.css?v=1699123456"> *}
Wenn Sie die Datei aktualisieren, ändert sich der Zeitstempel und erzwingt eine Aktualisierung des Browser-Caches.
Die Versionierung pro Asset steuern:
// Versionierung für ein bestimmtes Asset abschalten
$asset = $assets->getAsset('style.css', ['version' => false]);
{* In Latte *}
{asset 'style.css', version: false}
Dieselbe Syntax reference, key: value übergibt Optionen auch an n:asset und
{preload}:
<img n:asset="photo.jpg, version: false">
{preload 'style.css', version: false}
Schrift-Assets
Schriften erhalten eine besondere Behandlung mit korrektem CORS:
{* Korrektes Preload mit crossorigin *}
{preload 'fonts:OpenSans-Regular.woff2'}
{* Verwendung in CSS *}
<style>
@font-face {
font-family: 'Open Sans';
src: url('{asset 'fonts:OpenSans-Regular.woff2'}') format('woff2');
font-display: swap;
}
</style>
Eigene Mapper
Erstellen Sie eigene Mapper für besondere Anforderungen wie Cloud-Speicher oder dynamische Generierung:
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);
}
}
In der Konfiguration registrieren:
assets:
mapping:
cloud: CloudStorageMapper(@cloudClient, 'my-bucket')
Wie jeden anderen Mapper verwenden:
{asset 'cloud:user-uploads/photo.jpg'}
Die Methode Helpers::createAssetFromUrl() erzeugt automatisch den richtigen Asset-Typ anhand der Dateiendung.
Asset-Typen, die das Interface Nette\Assets\HtmlRenderable implementieren (Bilder, Skripte, Styles und so weiter),
kann {asset} als vollständiges HTML-Element rendern. Andere Dateitypen werden zu einem GenericAsset
(zum Beispiel ein PDF), das sich nicht als HTML-Element rendern lässt, aber trotzdem eine URL (und weitere Metadaten)
bereitstellt. Der Versuch, ein solches Asset als HTML-Element zu rendern, wirft eine Nette\InvalidArgumentException;
seine URL können Sie aber weiterhin innerhalb eines Attributs verwenden.