Integración con Vite
Las aplicaciones JavaScript modernas requieren herramientas de compilación sofisticadas. Nette Assets proporciona una integración de primera clase con Vite, la herramienta de compilación frontend de nueva generación. Consiga un desarrollo ultrarrápido con Hot Module Replacement (HMR) y compilaciones de producción optimizadas sin quebraderos de cabeza de configuración.
- Cero configuración: puente automático entre Vite y las plantillas PHP
- Gestión completa de las dependencias: una sola etiqueta se ocupa de todos los assets
- Hot Module Replacement: actualizaciones instantáneas de JavaScript y CSS
- Compilaciones de producción optimizadas: división del código y tree shaking
Nette Assets se integra perfectamente con Vite, así que obtiene todas estas ventajas mientras escribe sus plantillas como siempre.
Configurar Vite
Configuremos Vite paso a paso. No se preocupe si las herramientas de compilación son nuevas para usted, ¡lo explicaremos todo!
Paso 1: instalar Vite
Primero, instale Vite y el plugin de Nette en su proyecto:
npm install -D vite @nette/vite-plugin
Eso instala Vite y un plugin especial que ayuda a que Vite funcione perfectamente con Nette.
Paso 2: estructura del proyecto
El enfoque estándar es colocar los archivos fuente de los assets en una carpeta assets/ en la raíz del proyecto,
y las versiones compiladas en www/assets/:
web-project/ ├── assets/ ← archivos fuente (SCSS, TypeScript, imágenes fuente) │ ├── public/ ← archivos estáticos (se copian tal cual) │ │ └── favicon.ico │ ├── images/ │ │ └── logo.png │ ├── app.js ← punto de entrada principal │ └── style.css ← sus estilos └── www/ ← directorio público (document root) ├── assets/ ← aquí irán los archivos compilados └── index.php
La carpeta assets/ contiene sus archivos fuente, el código que escribe. Vite procesará esos archivos y pondrá
las versiones compiladas en www/assets/.
Paso 3: configurar Vite
Cree un archivo vite.config.ts en la raíz de su proyecto. Ese archivo le dice a Vite dónde encontrar los
archivos fuente y dónde poner los compilados.
El plugin de Vite de Nette trae valores predeterminados inteligentes que simplifican la configuración. Da por hecho que los
archivos fuente del frontend están en el directorio assets/ (opción root) y que los compilados van a
www/assets/ (opción outDir). Solo tiene que indicar el punto de
entrada:
import { defineConfig } from 'vite';
import nette from '@nette/vite-plugin';
export default defineConfig({
plugins: [
nette({
entry: 'app.js',
}),
],
});
Por debajo, además de root y outDir, el plugin establece algunas opciones más de Vite para que todo
encaje: base a '' (los assets se sirven directamente desde el document root),
build.manifest a true (para que Nette Assets pueda mapear los nombres de archivo con hash) y
build.assetsDir a '' (los archivos compilados van directamente a outDir, sin una subcarpeta
static/). Puede sobrescribir cualquiera de ellas.
El outDir predeterminado (www/assets) requiere que el directorio www/ ya
exista. Si no existe, el plugin se detiene con el error “The output directory … does not exist”.
Si quiere indicar otro nombre de directorio para compilar sus assets, tendrá que cambiar algunas opciones:
export default defineConfig({
root: 'assets', // directorio raíz de los assets fuente
build: {
outDir: '../www/assets', // adónde van los archivos compilados
},
// ... resto de la configuración ...
});
La ruta outDir se considera relativa a root, por eso lleva ../ al
principio.
Paso 4: configurar Nette
Hable a Nette Assets de Vite en su common.neon:
assets:
mapping:
default:
type: vite # le dice a Nette que use el ViteMapper
path: assets
Paso 5: añadir los scripts
Añada estos scripts a su package.json:
{
"scripts": {
"dev": "vite",
"build": "vite build"
}
}
Ahora puede:
npm run dev: arranca el servidor de desarrollo con recarga en calientenpm run build: crea los archivos de producción optimizados
Puntos de entrada
Un punto de entrada es el archivo principal donde arranca su aplicación. Desde ese archivo importa otros archivos (CSS, módulos de JavaScript, imágenes) y crea un árbol de dependencias. Vite sigue esas importaciones y lo empaqueta todo junto.
Ejemplo de punto de entrada assets/app.js:
// Importa los estilos
import './style.css'
// Importa los módulos de JavaScript
import netteForms from 'nette-forms';
import naja from 'naja';
// Inicializa la aplicación
netteForms.initOnLoad();
naja.initialize();
En la plantilla puede insertar un punto de entrada así:
{asset 'app.js'}
Nette Assets genera automáticamente todas las etiquetas HTML necesarias: JavaScript, CSS y cualquier otra dependencia.
Varios puntos de entrada
Las aplicaciones más grandes suelen necesitar puntos de entrada separados:
export default defineConfig({
plugins: [
nette({
entry: [
'app.js', // páginas públicas
'admin.js', // panel de administración
],
}),
],
});
Úselos en plantillas distintas:
{* En las páginas públicas *}
{asset 'app.js'}
{* En el panel de administración *}
{asset 'admin.js'}
Importante: archivos fuente frente a archivos compilados
Es fundamental entender que en producción solo puede cargar los archivos que Vite pone a disposición, ya sea a través de su manifiesto o copiándolos sin cambios desde la carpeta pública:
- Los puntos de entrada definidos en
entry(incluidos los módulos que importan dinámicamente) y los assets referenciados desde JavaScript o CSS (imágenes, fuentes, …): todos ellos quedan registrados en el manifiesto - Los archivos del directorio
assets/public/: estos no están en el manifiesto; se copian tal cual y{asset}los localiza mediante una búsqueda alternativa en el sistema de archivos
No puede cargar con {asset} archivos arbitrarios de assets/: si un archivo no se referencia en
ningún sitio, no se compilará. Si quiere que Vite conozca otros assets, puede moverlos a la carpeta pública.
Tenga en cuenta que, de forma predeterminada, Vite incrusta todos los assets menores de 4 KB, así que no podrá referenciar esos archivos directamente. (Vea la documentación de Vite).
{* ✓ Esto funciona: es un punto de entrada *}
{asset 'app.js'}
{* ✓ Esto funciona: está en assets/public/ *}
{asset 'favicon.ico'}
{* ✗ Esto no funcionará: un archivo cualquiera de assets/ *}
{asset 'components/button.js'}
Modo de desarrollo
El modo de desarrollo es totalmente opcional, pero aporta ventajas notables cuando se activa. La principal es el Hot Module Replacement (HMR): ve los cambios al instante sin perder el estado de la aplicación, lo que hace la experiencia de desarrollo mucho más fluida y rápida.
Vite es una herramienta de compilación moderna que hace el desarrollo increíblemente rápido. A diferencia de los empaquetadores tradicionales, durante el desarrollo Vite sirve su código directamente al navegador, lo que significa un arranque instantáneo del servidor por grande que sea su proyecto y actualizaciones ultrarrápidas.
Arrancar el servidor de desarrollo
Ejecute el servidor de desarrollo:
npm run dev
Verá:
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
Mantenga esta terminal abierta mientras desarrolla.
Mientras el servidor de desarrollo está en marcha, el plugin escribe un pequeño archivo de señal
www/assets/.vite/nette.json que contiene su URL. Nette Assets, del lado de PHP, lee ese archivo y pasa a cargar desde
el servidor de desarrollo cuando se cumplen ambas cosas:
- el servidor de desarrollo de Vite está en marcha (el archivo de señal existe), y
- su aplicación de Nette está en modo de depuración.
El resultado:
{asset 'app.js'}
{* En desarrollo: <script src="http://localhost:5173/@vite/client" type="module"></script>
<script src="http://localhost:5173/app.js" type="module"></script> *}
{* En producción: <script src="/assets/app-4f3a2b1c.js" type="module" crossorigin></script> *}
No hace falta configurar nada, ¡simplemente funciona! Para desactivar la detección o establecer la URL del servidor de desarrollo a mano, vea la opción devServer.
El archivo de señal está en www/assets/.vite/nette.json (justo al lado del
manifest.json de producción). Puede renombrarlo con la opción infoFile del plugin, cuyo valor
predeterminado es .vite/nette.json. Si Nette no detecta el servidor de desarrollo en marcha, compruebe que ese
archivo existe y apunta a la URL correcta.
Trabajar en dominios distintos
Si su servidor de desarrollo corre en algo que no sea localhost (como myapp.local), puede toparse con
problemas de CORS (Cross-Origin Resource Sharing). CORS es una función de seguridad de los navegadores web que bloquea de forma
predeterminada las peticiones entre dominios distintos. Cuando su aplicación PHP corre en myapp.local pero Vite
corre en localhost:5173, el navegador los ve como dominios distintos y bloquea las peticiones.
Tiene dos opciones para resolverlo:
Opción 1: configurar CORS
La solución más sencilla es permitir las peticiones entre orígenes desde su aplicación PHP:
export default defineConfig({
// ... resto de la configuración ...
server: {
cors: {
origin: 'http://myapp.local', // la URL de su aplicación PHP
},
},
});
Opción 2: ejecutar Vite en su dominio
La otra solución es hacer que Vite corra en el mismo dominio que su aplicación PHP.
export default defineConfig({
// ... resto de la configuración ...
server: {
host: 'myapp.local', // el mismo que su aplicación PHP
},
});
En realidad, incluso en este caso hay que configurar CORS, porque el servidor de desarrollo corre en el mismo hostname pero en otro puerto. Pero en este caso el CORS lo configura automáticamente el plugin de Vite de Nette.
Desarrollo con HTTPS
Si desarrolla con HTTPS, necesita certificados para su servidor de desarrollo de Vite. La forma más fácil es usar un plugin que genere los certificados automáticamente:
npm install -D vite-plugin-mkcert
Así se configura en vite.config.ts:
import mkcert from 'vite-plugin-mkcert';
export default defineConfig({
// ... resto de la configuración ...
plugins: [
mkcert(), // genera los certificados automáticamente y activa https
nette(),
],
});
Tenga en cuenta que, si usa la configuración de CORS (la opción 1 de arriba), tiene que actualizar la URL de origin para que
use https:// en lugar de http://.
Desarrollo con Docker
Cuando ejecuta Vite dentro de un contenedor de Docker hay que prestar atención a dos cosas: el navegador de su máquina tiene que poder llegar al servidor de desarrollo, y Vite tiene que detectar los cambios en los archivos a través de la frontera del contenedor.
Primero, publique el puerto de Vite del contenedor y enlace el servidor de desarrollo a todas las interfaces, para que sea accesible desde fuera del contenedor:
export default defineConfig({
// ... resto de la configuración ...
plugins: [
nette(),
],
server: {
host: '0.0.0.0', // escucha en todas las interfaces (obligatorio en un contenedor)
port: 5173, // tiene que coincidir con el puerto publicado
strictPort: true, // falla en lugar de elegir otro puerto
watch: {
usePolling: true, // actívelo si no se detectan los cambios en los volúmenes montados
},
},
});
El plugin escribe la URL del servidor de desarrollo en nette.json para el lado de PHP. Como
host: '0.0.0.0' no le sirve a un navegador (redirige a localhost en cada asset), el plugin la reescribe
automáticamente a localhost en esa URL, para que los assets se carguen correctamente.
Si abre la aplicación en un dominio propio en lugar de localhost, establezca la opción host del
plugin a ese dominio:
plugins: [
nette({ host: 'myapp.local' }), // el mismo dominio que su aplicación PHP
],
El plugin usa entonces ese host para la URL del servidor de desarrollo, lo añade a los orígenes de CORS y lo mete en la lista
blanca allowedHosts de Vite, así que funciona sin configurar CORS a mano.
Para configuraciones más complejas, por ejemplo cuando Vite corre detrás de un proxy inverso donde el host, el puerto y el
protocolo públicos difieren de la dirección interna, establezca server.origin de Vite a la URL pública completa.
El plugin la respeta y la escribe tal cual en nette.json, en lugar de deducir la URL del socket local:
server: {
origin: 'https://myapp.local:8443', // la URL pública donde el navegador llega a Vite
},
Compilaciones de producción
Cree los archivos de producción optimizados:
npm run build
Vite hará lo siguiente:
- Minificará todo el JavaScript y el CSS
- Dividirá el código en fragmentos óptimos
- Generará nombres de archivo con hash para invalidar la caché
- Creará un archivo de manifiesto para Nette Assets
Ejemplo de salida:
www/assets/
├── app-4f3a2b1c.js # Su JavaScript principal (minificado)
├── app-7d8e9f2a.css # CSS extraído (minificado)
├── vendor-8c4b5e6d.js # Dependencias compartidas
└── .vite/
└── manifest.json # Mapeo para Nette Assets
Los nombres de archivo con hash aseguran que los navegadores carguen siempre la última versión.
Carpeta pública
Los archivos del directorio assets/public/ se copian a la salida sin procesarlos:
assets/
├── public/
│ ├── favicon.ico
│ ├── robots.txt
│ └── images/
│ └── og-image.jpg
├── app.js
└── style.css
Referéncielos con normalidad:
{* Estos archivos se copian tal cual *}
<link rel="icon" href={asset 'favicon.ico'}>
<meta property="og:image" content={asset 'images/og-image.jpg'}>
En los archivos públicos puede usar las funciones de FilesystemMapper. La opción extension se aplica a las
referencias sin extensión (p. ej. {asset 'images/og-image'} encuentra primero og-image.webp):
assets:
mapping:
default:
type: vite
path: assets
extension: [webp, jpg, png] # para las referencias sin extensión
versioning: true # Añade la invalidación de caché
En la configuración de vite.config.ts puede cambiar la carpeta pública con la opción
publicDir.
Importaciones dinámicas
Vite divide automáticamente el código para cargarlo de forma óptima. Las importaciones dinámicas le permiten cargar código solo cuando se necesita de verdad, lo que reduce el tamaño del paquete inicial:
// Carga los componentes pesados bajo demanda
button.addEventListener('click', async () => {
let { Chart } = await import('./components/chart.js')
new Chart(data)
})
Las importaciones dinámicas crean fragmentos separados que se cargan solo cuando hacen falta. Esto se llama “code splitting” y es una de las funciones más potentes de Vite. Cuando usa importaciones dinámicas, Vite crea automáticamente archivos JavaScript separados para cada módulo importado dinámicamente.
La etiqueta {asset 'app.js'} no precarga automáticamente esos fragmentos dinámicos. Es un comportamiento
intencionado: no queremos descargar código que quizá no se use nunca. Los fragmentos se descargan solo cuando se ejecuta la
importación dinámica.
Pero, si sabe que ciertas importaciones dinámicas son críticas y harán falta pronto, puede precargarlas:
{* Punto de entrada principal *}
{asset 'app.js'}
{* Precarga las importaciones dinámicas críticas *}
{preload 'components/chart.js'}
Eso le dice al navegador que descargue el componente del gráfico en segundo plano, para que esté listo de inmediato cuando haga falta.
Soporte de TypeScript
TypeScript funciona nada más instalarlo:
// assets/main.ts
interface User {
name: string
email: string
}
export function greetUser(user: User): void {
console.log(`Hello, ${user.name}!`)
}
Referencie los archivos TypeScript con normalidad (como con cualquier archivo, main.ts tiene que ser un punto de
entrada):
{asset 'main.ts'}
Para tener soporte completo de TypeScript, instálelo:
npm install -D typescript
Configuración adicional de Vite
Aquí tiene algunas opciones útiles de la configuración de Vite con explicaciones detalladas:
export default defineConfig({
// Directorio raíz que contiene los assets fuente
root: 'assets',
// Carpeta cuyo contenido se copia tal cual al directorio de salida
// Predeterminado: 'public' (relativo a 'root')
publicDir: 'public',
build: {
// Adónde poner los archivos compilados (relativo a 'root')
outDir: '../www/assets',
// ¿Vaciar el directorio de salida antes de compilar?
// Útil para eliminar los archivos antiguos de compilaciones anteriores
emptyOutDir: true,
// Subdirectorio dentro de outDir para los fragmentos y assets generados
// Ayuda a organizar la estructura de la salida
assetsDir: 'static',
rollupOptions: {
// Punto (o puntos) de entrada: puede ser un solo archivo o un array de archivos
// Cada punto de entrada se convierte en un paquete separado
input: [
'app.js', // aplicación principal
'admin.js', // panel de administración
],
},
},
server: {
// Host al que enlazar el servidor de desarrollo
// Use '0.0.0.0' para exponerlo a la red
host: 'localhost',
// Puerto del servidor de desarrollo
port: 5173,
// Configuración de CORS para las peticiones entre orígenes
cors: {
origin: 'http://myapp.local',
},
},
css: {
// Activa los source maps de CSS en desarrollo
devSourcemap: true,
},
plugins: [
nette(),
],
});
Ojo con las rutas de los puntos de entrada de rollupOptions.input de arriba: Rollup resuelve las
rutas relativas respecto al directorio de trabajo actual (la raíz de su proyecto), no respecto a
root: 'assets'. Así que un 'app.js' a secas no existirá en tiempo de compilación. Use la opción
entry del plugin (que resuelve las rutas de los puntos de entrada relativas a root) o escriba las rutas
relativas a la raíz del proyecto, p. ej. 'assets/app.js'.
¡Eso es todo! Ya tiene un sistema de compilación moderno integrado con Nette Assets.