Livewire 4: componentes de un archivo, islas y el peaje de migrar
Livewire 4 es el mayor cambio de la librería desde la v3 y, a diferencia de Laravel 13, no es una actualización tranquila. Hay capacidades genuinamente nuevas y una lista de migración lo bastante larga como para merecer planificación.
Los componentes pueden ser un solo archivo
El cambio principal es el formato de componente. Junto al esquema tradicional de clase más vista, ahora pueden escribirse como un único archivo que combina PHP y Blade, o como componentes multiarchivo. Vienen acompañados de slots y reenvío automático de atributos, lo que acerca mucho la composición al comportamiento de los componentes Blade normales.
El JavaScript también puede vivir directamente en los componentes basados en vista, sin envolverlo en @script. Es un detalle pequeño, pero elimina una de las molestias más persistentes de la v3.
Islas
Las islas permiten que una región de un componente se actualice con independencia del resto. En la v3, cambiar una propiedad re-renderizaba el componente entero y comparaba el resultado; las islas reducen eso a la región que realmente cambió.
Esto importa sobre todo en páginas donde una pieza pequeña está ocupada (un contador, un estado en vivo, un valor consultado periódicamente) dentro de un componente que por lo demás es caro de renderizar. Antes la solución era extraer un componente hijo casi únicamente para proteger al padre de re-renderizarse. Las islas atacan eso directamente.
Las peticiones dejan de bloquearse entre sí
Varios cambios apuntan en la misma dirección:
- Las acciones asíncronas pueden ejecutarse en paralelo si lo activas con
wire:click.asynco el atributo#[Async]. Sirven para acciones que solo producen efectos secundarios, como registrar actividad; por defecto, las acciones siguen encolándose una tras otra - El polling no bloquea, así que una consulta lenta ya no retrasa una interacción del usuario
- Las actualizaciones en vivo van en paralelo
Cualquiera que haya visto un poll de 300 ms colocarse por delante de la pulsación de un botón reconocerá el problema que se resuelve.
Directivas nuevas
wire:sort: ordenación por arrastrar y soltar sin recurrir a otra libreríawire:ref: referencias a elementos sin inventarse identificadores- Atributos
data-loading: los estados de carga pasan a ser cuestión de estilos, no de marcado
wire:intersect (se dispara al entrar en el viewport, con modificadores .once, .half y .full) no es nuevo en la v4; ya existía y simplemente está documentado junto a las directivas nuevas.
$errors está ahora disponible como propiedad mágica desde JavaScript, lo que cierra un hueco que antes obligaba a pasar el estado de validación a mano.
La lista de migración
Aquí es donde conviene invertir la planificación.
Cambió el enrutado. Route::livewire() es ahora el método recomendado para componentes de página completa, y es obligatorio para que los componentes de un solo archivo y multiarchivo funcionen como página completa. Route::get(Component::class) sigue funcionando para componentes de clase.
wire:model redujo su alcance. Ahora solo escucha eventos originados en el propio elemento. Si dependías de capturar eventos de elementos hijos, añade .deep para recuperarlo.
Cambió el comportamiento de los modificadores. .blur y .change controlan también la sincronización en el cliente. Para recuperar el comportamiento de la v3, antepón .live: wire:model.live.blur.
Se movieron las URLs internas. Los endpoints de Livewire pasan de /livewire/ a /livewire-{hash}/. Cualquier lista blanca, regla de CDN o excepción de WAF necesita actualizarse: este es el cambio con más probabilidad de romperse en producción y no en desarrollo.
Las transiciones son nativas. wire:transition usa la View Transitions API del navegador en lugar de Alpine, y desaparecen todos los modificadores personalizados (.opacity, .scale, .duration).
Claves de configuración renombradas. layout pasa a component_layout y usa el espacio de nombres layouts::; lazy_placeholder pasa a component_placeholder; smart_wire_keys ahora vale true por defecto.
Cambios en la API de JavaScript. $wire.$js() queda obsoleto en favor de $wire.$js.actionName = () => {}. Los hooks commit y request quedan obsoletos en favor de interceptMessage() e interceptRequest(). Las formas antiguas siguen funcionando por ahora, pero conviene migrar cuando puedas.
Las etiquetas de componente deben cerrarse. Las etiquetas sin cerrar no se renderizarán con el soporte de slots activado.
Además, stream() cambió de firma: de stream(to:, content:, replace:) a stream(content:, replace:, el:).
¿Merece la pena?
Si construyes interfaces interactivas con Livewire, sí: las islas y las peticiones en paralelo atacan quejas reales de rendimiento, y los componentes de un solo archivo eliminan mucha ceremonia.
Si tu uso de Livewire es superficial, sé honesto al respecto antes de agendar la migración. Nosotros eliminamos Livewire por completo de este sitio en una reconstrucción reciente: sostenía un único botón de modo oscuro, sin componentes propios, así que la dependencia costaba más de lo que aportaba. Hubo además una segunda razón: Flux, que necesitaba Livewire, bloqueaba la actualización a Laravel 13, y por eso Livewire se fue. Alpine cubre ese terreno con una fracción del peso.
Livewire 4 es una versión sólida. También es una actualización real, y solo compensa donde de verdad estás usando lo que ofrece.