Saltearse al contenido

Hydration

Usar Livewire se siente como adjuntar una clase PHP del lado del servidor directamente a un navegador web. Cosas como llamar a funciones del lado del servidor directamente desde pulsaciones de botón apoyan esta ilusión. Pero en realidad, es sólo eso: una ilusión.

En el fondo, Livewire se comporta mucho más como una aplicación web estándar. Renderiza HTML estático al navegador, escucha los eventos del navegador, y luego hace peticiones AJAX para invocar código del lado del servidor.

Debido a que cada petición AJAX que Livewire hace al servidor es “sin estado” (lo que significa que no hay un proceso backend de larga duración que mantenga vivo el estado de un componente), Livewire debe recrear el último estado conocido de un componente antes de hacer cualquier actualización.

Hace esto tomando “instantáneas” del componente PHP después de cada actualización del lado del servidor para que el componente pueda ser recreado o reanudado en la siguiente petición.

A lo largo de esta documentación, nos referiremos al proceso de tomar la instantánea como “deshidratación” y al proceso de recrear un componente a partir de una instantánea como “hidratación”.

Deshidratando

Cuando Livewire deshidrata un componente del lado del servidor, hace dos cosas:

  • Renderiza la plantilla del componente a HTML
  • Crea una instantánea JSON del componente.

Renderizando HTML

Después de montar un componente o de realizar una actualización, Livewire llama al método render() de un componente para convertir la plantilla Blade a HTML sin procesar.

Tomemos como ejemplo el siguiente componente Counter:

class Counter extends Component
{
public $count = 1;
public function increment()
{
$this->count++;
}
public function render()
{
return <<<'HTML'
<div>
Count: {{ $count }}
<button wire:click="increment">+</button>
</div>
HTML;
}
}

Después de cada montaje o actualización, Livewire convertiría el componente Counter anterior en el siguiente HTML:

<div>
Count: 1
<button wire:click="increment">+</button>
</div>

La instantánea

Para volver a crear el componente Counter en el servidor durante la siguiente solicitud, se crea una instantánea JSON, intentando capturar la mayor parte posible del estado del componente:

{
state: {
count: 1,
},
memo: {
name: 'counter',
id: '1526456',
},
}

Fíjate en las dos partes de la instantánea: memo y state.

La parte memo se utiliza para almacenar la información necesaria para identificar y recrear el componente, mientras que la parte state almacena los valores de todas las propiedades públicas del componente.

Incrustar la instantánea en el HTML

Cuando un componente se renderiza por primera vez, Livewire almacena la instantánea como JSON dentro de un atributo HTML llamado wire:snapshot. De esta forma, el núcleo JavaScript de Livewire puede extraer el JSON y convertirlo en un objeto en tiempo de ejecución:

<div wire:id="..." wire:snapshot="{ state: {...}, memo: {...} }">
Count: 1
<button wire:click="increment">+</button>
</div>

Hidratación

Cuando se activa la actualización de un componente, por ejemplo, se pulsa el botón ”+” en el componente Contador, se envía al servidor una carga útil como la siguiente:

{
calls: [
{ method: 'increment', params: [] },
],
snapshot: {
state: {
count: 1,
},
memo: {
name: 'counter',
id: '1526456',
},
}
}

Antes de que Livewire pueda llamar al método de increment, primero debe crear una nueva instancia de Contador y sembrarla con el estado de la instantánea.

Aquí hay algo de pseudo-código PHP que logra este resultado:

$state = request('snapshot.state');
$memo = request('snapshot.memo');
$instance = Livewire::new($memo['name'], $memo['id']);
foreach ($state as $property => $value) {
$instance[$property] = $value;
}

Si sigue el script anterior, verá que después de crear un objeto Counter, sus propiedades públicas se establecen basándose en el estado proporcionado desde la instantánea.

Hidratación avanzada

El ejemplo anterior de Counter funciona bien para demostrar el concepto de hidratación; sin embargo, sólo demuestra cómo Livewire maneja la hidratación de valores simples como los enteros (1).

Como ya sabrá, Livewire soporta muchos tipos de propiedades más sofisticadas que los enteros.

Echemos un vistazo a un ejemplo ligeramente más complejo - un componente Todos:

class Todos extends Component
{
public $todos;
public function mount() {
$this->todos = collect([
'first',
'second',
'third',
]);
}
}

Como puedes ver, estamos estableciendo la propiedad $todos a una colección Laravel con tres cadenas como contenido.

JSON por sí solo no tiene forma de representar colecciones Laravel, así que en su lugar, Livewire ha creado su propio patrón de asociación de metadatos con datos puros dentro de una instantánea.

Aquí está el objeto de estado de la instantánea para este componente Todos:

state: {
todos: [
[ 'first', 'second', 'third' ],
{ s: 'clctn', class: 'Illuminate\\Support\\Collection' },
],
},

Esto puede confundirle si esperaba algo más sencillo, como:

state: {
todos: [ 'first', 'second', 'third' ],
},

Sin embargo, si Livewire estuviera hidratando un componente basado en estos datos, no tendría forma de saber que es una colección y no un array normal.

Por lo tanto, Livewire soporta una sintaxis de estado alternativa en forma de tupla (un array de dos elementos):

todos: [
[ 'first', 'second', 'third' ],
{ s: 'clctn', class: 'Illuminate\\Support\\Collection' },
],

Cuando Livewire encuentra una tupla al hidratar el estado de un componente, utiliza la información almacenada en el segundo elemento de la tupla para hidratar de forma más inteligente el estado almacenado en el primero.

Para demostrarlo más claramente, aquí hay un código simplificado que muestra cómo Livewire podría recrear una propiedad de colección basada en la instantánea anterior:

[ $state, $metadata ] = request('snapshot.state.todos');
$collection = new $metadata['class']($state);

Como puedes ver, Livewire utiliza los metadatos asociados al estado para derivar la clase de colección completa.

Tuplas profundamente anidadas

Una clara ventaja de este enfoque es la capacidad de deshidratar e hidratar propiedades profundamente anidadas.

Por ejemplo, consideremos el ejemplo anterior de Todos, excepto que ahora con un Laravel Stringable en lugar de una cadena simple como tercer elemento de la colección:

class Todos extends Component
{
public $todos;
public function mount() {
$this->todos = collect([
'first',
'second',
str('third'),
]);
}
}

La instantánea deshidratada para el estado de este componente tendría ahora este aspecto:

todos: [
[
'first',
'second',
[ 'third', { s: 'str' } ],
],
{ s: 'clctn', class: 'Illuminate\\Support\\Collection' },
],

Como puede ver, el tercer elemento de la colección se ha deshidratado en una tupla de metadatos. El primer elemento de la tupla es el valor de cadena plano, y el segundo es una bandera que denota a Livewire que esta cadena es un stringable.

Soporte de tipos de propiedades personalizadas

Internamente, Livewire tiene soporte de hidratación para los tipos más comunes de PHP y Laravel. Sin embargo, si deseas soportar tipos no soportados, puedes hacerlo usando Sintetizadores - el mecanismo interno de Livewire para hidratar/deshidratar tipos de propiedades no primitivas.