Гидратация⚓︎
Работа с Livewire ощущается так, будто PHP-класс с сервера подключен напрямую к браузеру. Такие вещи, как вызов серверных функций при нажатии кнопок, поддерживают эту иллюзию. Но на самом деле это именно иллюзия.
За кулисами Livewire ведет себя гораздо больше как стандартное веб-приложение. Он отрисовывает статический HTML в браузере, слушает события браузера, а затем делает AJAX-запросы для вызова серверного кода.
Поскольку каждый AJAX-запрос, который Livewire отправляет на сервер, «не сохраняет состояние», что означает отсутствие долгоиграющего фонового процесса, поддерживающего состояние компонента, Livewire должен воссоздать последнее известное состояние компонента перед внесением каких-либо обновлений.
Он делает это, создавая «снимки» PHP-компонента после каждого обновления на стороне сервера, чтобы компонент можно было воссоздать или возобновить при следующем запросе.
В этой документации мы будем называть процесс создания снимка «дегидратацией» (dehydration), а процесс воссоздания компонента из снимка — «гидратацией» (hydration).
Дегидратация⚓︎
Когда Livewire дегидратирует серверный компонент, он делает две вещи:
- Отрисовывает шаблон компонента в HTML
- Создает JSON-снимок компонента
Отрисовка HTML⚓︎
После того как компонент смонтирован или внесено обновление, Livewire вызывает метод render() компонента, чтобы преобразовать шаблон Blade в чистый HTML.
Рассмотрим в качестве примера компонент counter:
<?php
use Livewire\Component;
new class extends Component {
public $count = 1;
public function increment()
{
$this->count++;
}
public function render()
{
return <<<'HTML'
<div>
Счётчик: {{ $count }}
<button wire:click="increment">+</button>
</div>
HTML;
}
};
После каждого монтажа или обновления Livewire отрисует компонент counter в следующий HTML:
Снимок⚓︎
Чтобы воссоздать компонент counter на сервере во время следующего запроса, создается JSON-снимок, пытающийся запечатлеть как можно больше состояния компонента:
Обратите внимание на две части снимка: memo и state.
Часть memo используется для хранения информации, необходимой для идентификации и воссоздания компонента, а часть state хранит значения всех публичных свойств компонента.
Информация
Приведённый выше снимок является сокращённой версией реального снимка в Livewire. В живых приложениях снимок содержит гораздо больше информации, такой как ошибки валидации, список дочерних компонентов, локали и многое другое. Для более детального ознакомления с объектом снимка вы можете обратиться к документации схемы снимка.
Внедрение снимка в HTML⚓︎
При первой отрисовке компонента Livewire сохраняет снимок в виде JSON внутри HTML-атрибута с именем wire:snapshot. Таким образом, JavaScript-ядро Livewire может извлечь JSON и превратить его в объект времени выполнения:
<div wire:id="..." wire:snapshot="{ state: {...}, memo: {...} }">
Счётчик: 1
<button wire:click="increment">+</button>
</div>
Гидратация⚓︎
Когда инициируется обновление компонента, например, нажата кнопка «+» в компоненте counter, на сервер отправляются данные, подобные следующим:
{
calls: [
{ method: 'increment', params: [] },
],
snapshot: {
state: {
count: 1,
},
memo: {
name: 'counter',
id: '1526456',
},
}
}
Прежде чем Livewire сможет вызвать метод increment, он должен сначала создать новый экземпляр counter и заполнить его состоянием из снимка.
Вот PHP-псевдокод, который достигает этого результата:
<?php
$state = request('snapshot.state');
$memo = request('snapshot.memo');
$instance = Livewire::new($memo['name'], $memo['id']);
foreach ($state as $property => $value) {
$instance[$property] = $value;
}
Если вы проследите за приведённым выше скриптом, вы увидите, что после создания объекта counter его публичные свойства устанавливаются на основе состояния, предоставленного из снимка.
Продвинутая гидратация⚓︎
Пример с counter хорошо демонстрирует концепцию гидратации, однако он показывает только то, как Livewire обрабатывает гидратацию простых значений, таких как целые числа (1).
Как вы, возможно, знаете, Livewire поддерживает гораздо более сложные типы свойств, помимо целых чисел.
Давайте рассмотрим чуть более сложный пример — компонент todos:
<?php
use Livewire\Component;
new class extends Component {
public $todos;
public function mount() {
$this->todos = collect([
'first',
'second',
'third',
]);
}
};
Как видите, мы устанавливаем свойство $todos в коллекцию Laravel с тремя строками в качестве содержимого.
Сам по себе JSON не имеет способа представления коллекций Laravel, поэтому Livewire создал собственный шаблон ассоциации метаданных с чистыми данными внутри снимка.
Вот объект состояния снимка для этого компонента todos:
state: {
todos: [
[ 'first', 'second', 'third' ],
{ s: 'clctn', class: 'Illuminate\\Support\\Collection' },
],
},
Это может показаться странным, если вы ожидали чего-то более прямолинейного, например:
Однако если бы Livewire гидратировал компонент на основе этих данных, у него не было бы способа узнать, что это коллекция, а не обычный массив.
Поэтому Livewire поддерживает альтернативный синтаксис состояния в виде кортежа (массива из двух элементов):
todos: [
[ 'first', 'second', 'third' ],
{ s: 'clctn', class: 'Illuminate\\Support\\Collection' },
],
Когда Livewire встречает кортеж при гидратации состояния компонента, он использует информацию, хранящуюся во втором элементе кортежа, чтобы более интеллектуально гидратировать состояние, хранящееся в первом.
Для более наглядной демонстрации приведем упрощённый код, показывающий, как Livewire может воссоздать свойство коллекции на основе вышеуказанного снимка:
<?php
[ $state, $metadata ] = request('snapshot.state.todos');
$collection = new $metadata['class']($state);
Как видите, Livewire использует метаданные, связанные с состоянием, для получения полного класса коллекции.
Глубоко вложенные кортежи⚓︎
Одним из явных преимуществ этого подхода является возможность дегидратации и гидратации глубоко вложенных свойств.
Например, рассмотрим тот же пример todos, но теперь с Laravel Stringable вместо обычной строки в качестве третьего элемента коллекции:
<?php
use Livewire\Component;
new class extends Component {
public $todos;
public function mount() {
$this->todos = collect([
'first',
'second',
str('third'),
]);
}
};
Дегидратированный снимок состояния этого компонента теперь будет выглядеть так:
todos: [
[
'first',
'second',
[ 'third', { s: 'str' } ],
],
{ s: 'clctn', class: 'Illuminate\\Support\\Collection' },
],
Как видите, третий элемент коллекции был дегидратирован в кортеж метаданных. Первый элемент кортежа — это обычное строковое значение, а второй — флаг, указывающий Livewire, что эта строка является stringable (объектом строки).
Поддержка пользовательских типов свойств⚓︎
Внутренне Livewire поддерживает гидратацию большинства распространённых типов PHP и Laravel. Однако если вы хотите поддерживать типы, которые не поддерживаются «из коробки», вы можете сделать это с помощью синтезаторов — внутреннего механизма Livewire для гидратации/дегидратации не примитивных типов свойств.
Смотрите также⚓︎
- Хуки жизненного цикла — использование хуков hydrate() и dehydrate()
- Свойства — как свойства сохраняются между запросами
- Морфинг — как Livewire обновляет DOM
- Синтезаторы — настройка сериализации свойств