Morphing
Cuando un componente Livewire actualiza el DOM del navegador, lo hace de una forma inteligente que llamamos “morphing”. El término morph contrasta con una palabra como reemplazar.
En lugar de reemplazar el HTML de un componente con el nuevo HTML renderizado cada vez que se actualiza un componente, Livewire compara dinámicamente el HTML actual con el nuevo HTML, identifica las diferencias y realiza cambios quirúrgicos en el HTML sólo en los lugares donde los cambios son necesarios.
Esto tiene la ventaja de preservar los elementos existentes y no modificados en un componente. Por ejemplo, los escuchadores de eventos, el estado de enfoque y los valores de entrada de formularios se conservan entre actualizaciones de Livewire. Por supuesto, morphing también ofrece un mayor rendimiento en comparación con borrar y volver a renderizar el nuevo DOM en cada actualización.
Cómo funciona el morphing
Para entender cómo Livewire determina qué elementos actualizar entre peticiones Livewire, considere este simple componente Tareas:
class Tareas extends Component{ public $tarea = '';
public $tareas = [ 'primera', 'segunda', ];
public function agregar() { $this->tareas[] = $this->tarea; }}<form wire:submit="agregar"> <ul> @foreach ($tareas as $elemento) <li>{{ $elemento }}</li> @endforeach </ul>
<input wire:model="tarea"></form>El renderizado inicial de este componente mostrará el siguiente HTML:
<form wire:submit="agregar"> <ul> <li>primera</li>
<li>segunda</li> </ul>
<input wire:model="tarea"></form>Ahora, imagine que escribe “tercera” en el campo de entrada y pulsa la tecla [Intro]. El nuevo HTML sería:
<form wire:submit="agregar"> <ul> <li>primera</li>
<li>segunda</li>
<li>tercera</li> </ul>
<input wire:model="tarea"></form>Cuando Livewire procesa la actualización del componente, transforma el DOM original en el nuevo HTML renderizado.
Como puede ver, Livewire recorre ambos árboles HTML simultáneamente. A medida que encuentra cada elemento en ambos árboles, los compara en busca de cambios, adiciones y eliminaciones. Si detecta alguno, realiza quirúrgicamente el cambio apropiado.
Defectos del morphing
Los siguientes son escenarios en los que los algoritmos de morphing no identifican correctamente el cambio en los árboles HTML y, por lo tanto, causan problemas en su aplicación.
Inserción de elementos intermedios
Considere la siguiente plantilla Livewire Blade para un componente CreatePost ficticio:
<form wire:submit="guardar"> <div> <input wire:model="titulo"> </div>
@if ($errors->has('titulo')) <div>{{ $errors->first('titulo') }}</div> @endif
<div> <button>Guardar</button> </div></form>Como puedes ver, cuando Livewire encuentra el nuevo <div> para el mensaje de error, no sabe si cambiar el <div> existente en su lugar, o insertar el nuevo <div> en el medio.
Para reiterar lo que está pasando más explícitamente:
- Livewire encuentra el primer
<div>en ambos árboles. Son iguales, así que continúa. - Livewire encuentra el segundo
<div>en ambos árboles y piensa que son el mismo<div>, sólo que uno ha cambiado de contenido. Así que en lugar de insertar el mensaje de error como un nuevo elemento, cambia el<button>en un mensaje de error. - Livewire entonces, después de modificar erróneamente el elemento anterior, nota un elemento adicional al final de la comparación. Entonces crea y añade el elemento después del anterior.
- Por lo tanto, destruye y luego vuelve a crear un elemento que, de otro modo, debería haber sido simplemente movido.
Este escenario está en la raíz de casi todos los errores relacionados con morph.
Aquí hay algunos impactos problemáticos específicos de estos errores:
- Los escuchadores de eventos y el estado de los elementos se pierden entre actualizaciones
- Los escuchadores de eventos y el estado se colocan erróneamente en los elementos equivocados
- Componentes enteros de Livewire pueden ser reiniciados o duplicados ya que los componentes de Livewire son también simplemente elementos en el árbol DOM
- Los componentes de Alpine y el estado pueden perderse o colocarse erróneamente
- Afortunadamente, Livewire ha trabajado duro para mitigar estos problemas utilizando los siguientes enfoques:
Look-ahead interno
Livewire tiene un paso adicional en su algoritmo de morphing que comprueba los elementos subsiguientes y su contenido antes de cambiar un elemento.
Esto evita que el escenario anterior ocurra en muchos casos.
Inyectando marcadores de morph
En el backend, Livewire detecta automáticamente los condicionales dentro de las plantillas Blade y los envuelve en marcadores de comentarios HTML que el JavaScript de Livewire puede usar como guía al morphear.
Aquí hay un ejemplo de la plantilla Blade anterior pero con los marcadores inyectados de Livewire:
<form wire:submit="guardar"> <div> <input wire:model="titulo"> </div>
<!--[if BLOCK]><![endif]--> @if ($errors->has('titulo')) <div>Error: {{ $errors->first('titulo') }}</div> @endif <!--[if ENDBLOCK]><![endif]-->
<div> <button>Guardar</button> </div></form>Con estos marcadores inyectados en la plantilla, Livewire puede ahora detectar más fácilmente la diferencia entre un cambio y una adición.
Esta característica es extremadamente beneficiosa para las aplicaciones Livewire, pero debido a que requiere analizar las plantillas mediante regex, a veces puede fallar a la hora de detectar correctamente las condicionales. Si esta característica es más un estorbo que una ayuda para su aplicación, puede desactivarla con la siguiente configuración en el archivo config/livewire.php de su aplicación:
'inject_morph_markers' => false,Envolver condicionales
Si las dos soluciones anteriores no cubren su situación, la forma más fiable de evitar problemas de morphing es envolver condicionales y bucles en sus propios elementos que siempre están presentes.
Por ejemplo, aquí está la plantilla Blade anterior reescrita con elementos <div> envolventes:
<form wire:submit="guardar"> <div> <input wire:model="titulo"> </div>
<div> @if ($errors->has('titulo')) <div>{{ $errors->first('titulo') }}</div> @endif </div>
<div> <button>Guardar</button> </div></form>Ahora que el condicional ha sido envuelto en un elemento persistente, Livewire morfeará los dos diferentes árboles HTML correctamente.
Pasando por alto el morphing
Si necesitas pasar por alto el morphing completamente para un elemento, puedes usar wire:replace para ordenar a livewire que reemplace todos los hijos de un elemento en lugar de intentar morphear los elementos existentes.