Nette Assets

¿Cansado de gestionar manualmente archivos estáticos en sus aplicaciones web? Olvídese de codificar rutas, lidiar con la invalidación de la caché o preocuparse por el versionado de archivos. Nette Assets transforma la forma en que trabaja con imágenes, hojas de estilo, scripts y otros recursos estáticos.

  • El versionado inteligente garantiza que los navegadores siempre carguen los archivos más recientes
  • Detección automática de tipos de archivo y dimensiones
  • Integración perfecta con Latte con etiquetas intuitivas
  • Arquitectura flexible que soporta sistemas de archivos, CDNs y Vite
  • Carga diferida para un rendimiento óptimo

¿Por qué Nette Assets?

Trabajar con archivos estáticos a menudo significa código repetitivo y propenso a errores. Usted construye URLs manualmente, añade parámetros de versión para la eliminación de caché y maneja diferentes tipos de archivos de manera distinta. Esto lleva a un código como:

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

Con Nette Assets, toda esta complejidad desaparece:

{* Todo automatizado - URL, versionado, dimensiones *}
<img n:asset="images/logo.png">
<link n:asset="css/style.css">

{* O simplemente *}
{asset 'css/style.css'}

¡Eso es todo! La librería automáticamente:

  • Añade parámetros de versión basados en la hora de modificación del archivo
  • Detecta las dimensiones de la imagen y las incluye en el HTML
  • Genera el elemento HTML correcto para cada tipo de archivo
  • Maneja tanto entornos de desarrollo como de producción

Instalación

Instale Nette Assets usando Composer:

composer require nette/assets

Requiere PHP 8.1 o superior y funciona perfectamente con Nette Framework, pero también puede usarse de forma independiente.

Primeros Pasos

Nette Assets funciona de forma inmediata sin configuración. Coloque sus archivos estáticos en el directorio www/assets/ y empiece a usarlos:

{* Muestra una imagen con dimensiones automáticas *}
{asset 'logo.png'}

{* Incluye una hoja de estilo con versionado *}
{asset 'style.css'}

{* Carga un módulo JavaScript *}
{asset 'app.js'}

Para un mayor control sobre el HTML generado, use el atributo n:asset o la función asset().

Cómo Funciona

Nette Assets se construye alrededor de tres conceptos fundamentales que lo hacen potente y, al mismo tiempo, sencillo de usar:

Assets – Sus Archivos Hechos Inteligentes

Un asset representa cualquier archivo estático en su aplicación. Cada archivo se convierte en un objeto con propiedades de solo lectura útiles:

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

Diferentes tipos de archivo proporcionan diferentes propiedades:

  • Imágenes: ancho, alto, texto alternativo, carga diferida
  • Scripts: tipo de módulo, hashes de integridad, crossorigin
  • Hojas de estilo: media queries, integridad
  • Audio/Video: duración, dimensiones
  • Fuentes: precarga adecuada con CORS

La librería detecta automáticamente los tipos de archivo y crea la clase de asset apropiada.

Mappers – De Dónde Vienen los Archivos

Un mapper sabe cómo encontrar archivos y crear URLs para ellos. Puede tener varios mappers para diferentes propósitos: archivos locales, CDN, almacenamiento en la nube o herramientas de construcción (cada uno de ellos tiene un nombre). El FilesystemMapper incorporado maneja los archivos locales, mientras que ViteMapper se integra con las herramientas de construcción modernas.

Los mappers se definen en la configuración.

Registry – Su Interfaz Principal

El registry gestiona todos los mappers y proporciona la API principal:

// Inyecta el registro en su servicio
public function __construct(
	private Nette\Assets\Registry $assets
) {}

// Obtiene assets de diferentes mappers
$logo = $this->assets->getAsset('images:logo.png'); // mapper 'image'
$app = $this->assets->getAsset('app:main.js'); // mapper 'app'
$style = $this->assets->getAsset('style.css'); // usa el mapper predeterminado

El registro selecciona automáticamente el mapper correcto y almacena en caché los resultados para mejorar el rendimiento.

Trabajando con Assets en PHP

El Registry proporciona dos métodos para recuperar assets:

// Lanza Nette\Assets\AssetNotFoundException si el archivo no existe
$logo = $assets->getAsset('logo.png');

// Devuelve null si el archivo no existe
$banner = $assets->tryGetAsset('banner.jpg');
if ($banner) {
	echo $banner->url;
}

Especificando Mappers

Puede elegir explícitamente qué mapper usar:

// Usar el mapper predeterminado
$file = $assets->getAsset('document.pdf');

// Usar un mapper específico con prefijo
$image = $assets->getAsset('images:photo.jpg');

// Usar un mapper específico con sintaxis de array
$script = $assets->getAsset(['scripts', 'app.js']);

Propiedades y Tipos de Assets

Cada tipo de asset proporciona propiedades de solo lectura relevantes:

// Propiedades de la imagen
$image = $assets->getAsset('photo.jpg');
echo $image->width;     // 1920
echo $image->height;    // 1080
echo $image->mimeType;  // 'image/jpeg'

// Propiedades del script
$script = $assets->getAsset('app.js');
echo $script->type;     // 'module' o null

// Propiedades del audio
$audio = $assets->getAsset('song.mp3');
echo $audio->duration;  // duración en segundos

// Todos los assets pueden convertirse a string (devuelve la URL)
$url = (string) $assets->getAsset('document.pdf');

Las propiedades como las dimensiones o la duración se cargan de forma diferida solo cuando se acceden, manteniendo la librería rápida.

Usando Assets en Plantillas Latte

Nette Assets proporciona una integración intuitiva con Latte mediante etiquetas y funciones.

{asset}

La etiqueta {asset} renderiza elementos HTML completos:

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

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

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

La etiqueta automáticamente:

  • Detecta el tipo de asset y genera el HTML apropiado
  • Incluye versionado para la eliminación de caché
  • Añade dimensiones para las imágenes
  • Establece los atributos correctos (type, media, etc.)

Cuando se usa dentro de atributos HTML, solo genera la URL:

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

n:asset

Para un control total sobre los atributos HTML:

{* El atributo n:asset rellena src, dimensiones, etc. *}
<img n:asset="product.jpg" alt="Producto" class="rounded">

{* Funciona con cualquier elemento relevante *}
<script n:asset="analytics.js" defer></script>
<link n:asset="print.css" media="print">
<audio n:asset="podcast.mp3" controls></audio>

Use variables y mappers:

{* Las variables funcionan de forma natural *}
<img n:asset="$product->image">

{* Especificar mapper con llaves *}
<img n:asset="images:{$product->image}">

{* Especificar mapper con notación de array *}
<img n:asset="[images, $product->image]">

asset()

Para una máxima flexibilidad, use la función asset():

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

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

Assets Opcionales

Maneje los assets que faltan con elegancia usando {asset?}, n:asset? y tryAsset():

{* Etiqueta opcional - no renderiza nada si falta el asset *}
{asset? 'optional-banner.jpg'}

{* Atributo opcional - se omite si falta el asset *}
<img n:asset?="user-avatar.jpg" alt="Avatar" class="avatar">

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

{preload}

Mejore el rendimiento de carga de la página:

{* En su sección <head> *}
{preload 'critical.css'}
{preload 'important-font.woff2'}
{preload 'hero-image.jpg'}

Genera enlaces de precarga apropiados:

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

Características Avanzadas

Detección Automática de Extensión

Maneje múltiples formatos automáticamente:

assets:
	mapping:
		images:
			path: img
			extension: [webp, jpg, png]  # Intentar en orden

Ahora puede solicitar sin extensión:

{* Encuentra logo.webp, logo.jpg o logo.png automáticamente *}
{asset 'images:logo'}

Perfecto para la mejora progresiva con formatos modernos.

Versionado Inteligente

Los archivos se versionan automáticamente según la hora de modificación:

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

Cuando actualiza el archivo, la marca de tiempo cambia, forzando la actualización de la caché del navegador.

Controle el versionado por asset:

// Deshabilitar el versionado para un asset específico
$asset = $assets->getAsset('style.css', ['version' => false]);

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

Assets de Fuentes

Las fuentes reciben un tratamiento especial con CORS adecuado:

{* Precarga adecuada con crossorigin *}
{preload 'fonts:OpenSans-Regular.woff2'}

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

Mappers Personalizados

Cree mappers personalizados para necesidades especiales como almacenamiento en la nube o generación dinámica:

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' no encontrado");
		}

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

Registre en la configuración:

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

Use como cualquier otro mapper:

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

El método Helpers::createAssetFromUrl() crea automáticamente el tipo de asset correcto basándose en la extensión del archivo.

Más información

versión: 1.0