Выпущена бета-версия Livewire v3

Источник: «The Livewire v3 Beta Has Been Released»
Калеб Порцио, выступая на конференции Laracon, только что выпустил официальную бета-версию Livewire v3! В своём выступлении он продемонстрировал множество новых возможностей, некоторые из которых мы рассмотрим здесь.

Переписанное ядро

Ядро Livewire было полностью переписано с нуля. Переписывание ядра было огромной работой, но это было необходимо для реализации всего того, что включено в данный релиз. Кроме того, новая архитектура ядра будет намного проще в поддержке для Калеба и участников проекта Livewire.

Использование Alpine

Ядро v3 теперь использует Alpine в полной мере. Вместо того чтобы включать код для применения обновлений DOM, добавления слушателей событий и т.д. в Livewire и Alpine, Livewire v3 использует Alpine для выполнения тяжёлой работы. Регистрируя Alpine-плагины, Livewire позволяет Alpine выполнять тяжёлую работу, обеспечивая при этом синтаксический сахар, который вы уже успели полюбить.

Это также означает, что Alpine теперь по умолчанию входит в состав Livewire, поэтому нет необходимости загружать Alpine через CDN или NPM. Он автоматически подключается.

Кроме того, в версии 3 для сравнения DOM и применения обновлений вместо morphdom используется плагин Alpine Morph. Это приведёт к уменьшению количества проблем с DOM-diffing и улучшению совместимости Livewire и Alpine.

Улучшена вложенность компонентов

В прошлом я считал вложенность компонентов Livewire антипаттерном или тем, чего следует избегать по возможности из-за потенциальных последствий для производительности. В версии 3 это уже не так!

Благодаря реактивным пропсам, пакетным запросам, новому свойству $parent и другим улучшениям, в v3 значительно улучшена вложенность компонентов. Некоторые из этих улучшений мы рассмотрим более подробно далее в этой статье.

Улучшенная сериализация свойств

В v2 была предпринята попытка интеллектуально определить свойства типа при первоначальном рендере, чтобы при последующих запросах вернуть их к тем же типам. Однако v2 не поддерживала глубоко вложенную гидратацию. Если у вас есть коллекция со смешанными типами, v2 не сможет вернуть каждому элементу коллекции его исходный тип. v3 гораздо умнее в этом отношении и сохраняет тип каждого элемента, чтобы при последующих запросах вернуть их к правильным типам.

Пакетные запросы

В версии 2, когда несколько компонентов на странице опрашивали или прослушивали одно и то же событие, Livewire посылал отдельные запросы для каждого компонента каждый раз, когда ему требовалось связаться с сервером. Версия 3 гораздо более эффективна и объединяет все эти обновления в один запрос.

Улучшенные настройки по умолчанию

Инъекция маркера в Blade

Расхождение DOM — одна из наиболее распространённых проблем, с которой можно столкнуться в v2. Обычно эта проблема возникала из-за того, что @if и аналогичные директивы Blade вставляли или удаляли элементы DOM. В v3 предпринята попытка обойти эти проблемы путём вставки HTML-комментариев (маркеров) в местах начала и окончания этих директив Blade. Ища эти маркеры, Livewire может сопоставить новые или удалённые элементы DOM с маркером, чтобы правильно разместить их в DOM.

<form wire:submit="save">
{{-- ... --}}

<!-- __BLOCK__ -->
@if ($success)
<div>Saved!</div>
@endif
<!-- ENDBLOCK -->

<div>
<button>Save</button>
</div>
<div>

wire:model по умолчанию отложена

Изначально живой Livewire был очень интересной функцией, поэтому его сделали по умолчанию. Поразмыслив и понаблюдав за реальным использованием, Калеб понял, что откладывать запросы wire:model лучше по умолчанию. Большинство приложений не нуждаются в синхронизации вводимых значений с сервером при каждом нажатии клавиши, поэтому в v3 поведение по умолчанию изменилось. Прежняя функциональность wire:model.defer стала новой по умолчанию при использовании wire:model. wire:model.live была добавлена для замены прежнего поведения по умолчанию для вводимых данных, которые действительно должны быть живыми.

{{-- Теперь отложено по умолчанию --}}
<input wire:model="name">

{{-- Синхронизация с сервером при каждом нажатии клавиши --}}
<input wire:model.live="name">

Новые возможности

Автоматическое подключение ресурсов

В версии 3 Livewire автоматически внедряет свои стили, скрипты и Alpine. Больше нет необходимости добавлять <livewire:styles /> и <livewire:scripts /> или загружать Alpine в свои проекты!

Новое пространство имён по умолчанию

По умолчанию в v3 используется пространство имён App\Livewire (и каталог app/Livewire), а не App\Http\Livewire. При желании можно настроить сохранение старого пространства имён.

Реактивные свойства

Некоторые пользователи Livewire, пришедшие из таких фронтенд-фреймворков, как React и Vue, автоматически предполагали, что свойства, которые они передают во вложенные компоненты, будут реагировать на изменения в родительском компоненте. В связи с некоторыми ограничениями в версии 2 это оказалось невозможным. Приходилось полагаться на события или другие обходные пути для синхронизации вложенных компонентов. В v3 добавлена поддержка "реактивных" пропсов. Это просто, как добавить атрибут #[Reactive] PHP к свойству в классе компонента.

<?php

// ...
use Livewire\Attributes\Reactive;

class InvoiceItem extends Component
{
#[Reactive]
public $item;
}

Объекты форм

Объекты форм — это новая концепция, которая позволяет поддерживать чистоту классов компонентов за счёт абстрагирования части кода, специфичного для формы. Вы можете захотеть поместить свойства формы компонента, валидацию, функции сохранения и т.д. в объекты формы.

Здесь приведён небольшой пример, но я рекомендую прочитать документацию для получения полного списка возможностей!

<?php

namespace App\Livewire\Forms;

use Livewire\Form;

class UserForm extends Form
{
#[Rule('required')]
public $name = '';

#[Rule(['required', 'email'])]
public $email = '';
}
<?php

namespace App\Livewire;

use Livewire\Component;
use App\Models\Post;
use App\Livewire\Forms\UserForm;

class UpdateProfile extends Component
{
public UserForm $form;

public function save()
{
auth()->user()->update(
$this->form->all()
);

return $this->redirect('/profile');
}

public function render()
{
return view('livewire.update-profile');
}
}
<form wire:submit="save">
<input type="text" wire:model="form.name">
<div>
@error('form.name')
<span class="error">{{ $message }}</span>
@enderror
</div>

<input type="email" wire:model="form.email">
<div>
@error('form.email')
<span class="error">{{ $message }}</span>
@enderror
</div>

<button type="submit">Save</button>
</form>

wire:navigate (режим SPA)

Ещё одно новое дополнение — wire:navigate. При использовании полностраничных компонентов Livewire теперь можно добавлять к ссылкам атрибут wire:navigate, что позволяет реализовать SPA-подобный опыт. Вместо загрузки всей страницы Livewire добавит индикатор загрузки в верхнюю часть страницы, получит содержимое новой страницы в фоновом режиме и затем интеллектуально подменит HTML на странице. Функция также поддерживает предварительную выборку и переадресацию, а также обеспечивает возможность работы с сохраняемыми элементами.

<a href="/profile" wire:navigate>Profile</a>

@persist

С добавлением wire:navigate теперь можно иметь сохраняемые элементы, которые не перезагружаются при переходе по различным страницам. Эта возможность будет полезна для таких случаев, как аудиоплееры, которые должны продолжать воспроизводить звук, пока пользователи переходят по разным страницам.

Чтобы воспользоваться этой возможностью, оберните элемент, который необходимо сохранить, директивой @persist:

@persist('player')
<audio src="{{ $episode->file }}" controls></audio>
@endpersist

Лениво загружаемые компоненты

Livewire теперь поддерживает компоненты с ленивой загрузкой. Иногда бывает так, что компонент, который может замедлять начальную загрузку страницы или изначально скрыт в модальном окне, необходимо лениво загрузить, чтобы начальная загрузка страницы была быстрой.

Для ленивой загрузки компонента добавьте атрибут lazy к компоненту в Blade.

<livewire:your-component lazy />

При первоначальной загрузке страницы Livewire пропустит рендеринг этого компонента. После загрузки всего остального содержимого страницы будет отправлен сетевой запрос на полный рендеринг компонента, и компонент будет вставлен в DOM.

Свойство $parent

Для взаимодействия с родительскими компонентами в предыдущих версиях Livewire приходилось использовать события и слушатели. В версии 3 появилось новое свойство $parent, которое можно использовать для вызова методов родительских компонентов непосредственно из дочерних.

<div>
<span>{{ $item->name }}</span>

<button wire:click="$parent.remove({{ $item->id }})">Remove</button>
</div>

Гибридные методы/запуск JS из бэкенда

В v3 появилась возможность писать JavaScript-методы в компонентах бэкенда. Добавив атрибут #[Js] к методу компонента, вы можете написать JavaScript-код в виде строки, и Livewire откроет этот метод для вашего фронтенда. При вызове метода через wire:click JavaScript будет выполняться на фронтенде без отправки сетевых запросов обратно на сервер.

<?php

// ...
use Livewire\Attributes\Js;

class UsersTable extends Component
{
public $filters = [];

#[Js]
public function reset()
{
return <<<'JS'
$wire.filters = [];
JS;
}

// ...
}
<div>
<div>
<input wire:model.live="filters.active" type="checkbox">
<label>Only active</label>
</div>

<button wire:click="reset">Reset Filters</button>

@foreach ($users as $user)
{{-- ... --}}
@endforeach
</div>

Иногда полезно выполнять небольшие фрагменты JavaScript из методов бэкенда. В v3 это можно сделать с помощью метода $this->js(). Просто вызовите этот метод и передайте строку JavaScript, и Livewire выполнит её на фронтенде при следующем рендере.

public function save()
{
// ...

$this->js("alert('Вы только что выполнили JS из бэкенда!')");
}

Использование PHP-атрибутов

Атрибуты PHP являются относительно новой и мощной функцией PHP, и в Livewire v3 они активно используются в новых и существующих функциях.

#[Url]

Новый атрибут #[Url] заменяет свойство $query из v2. Добавьте #[Url] над любым свойством компонента, и оно будет отслеживаться в строке запроса.

<?php

// ...
use Livewire\Attributes\Url;

class UsersTable extends Component
{
#[Url]
public $filters = [];

// ...
}

#[On]

Новый атрибут #[On] заменяет свойство $listeners из v2. Добавьте #[On('some-event')] над методом вашего компонента, и он будет выполняться при каждом событии.

<?php

// ...
use Livewire\Attributes\Url;

class UsersTable extends Component
{
// ...

#[On('filters-reset')]
public function resetFilters()
{
// ...
}
}

#[Layout] и #[Title]

Новые атрибуты #[Layout] и #[Title] позволяют задавать вид макета и заголовок для полностраничных компонентов. Они могут быть добавлены в метод рендеринга компонента или в сам класс.

#[Layout('layouts.app')]
public function render()
{
return view('livewire.create-post');
}

#[Computed]

На смену синтаксису getSomeProperty в v2 для вычисляемых свойств пришёл атрибут #[Computed]. Он функционирует так же, как и старый синтаксис, но имеет несколько новых, действительно мощных дополнений.

В v2 вычисляемые свойства можно было кэшировать только при одном запросе. В v3 свойство можно кэшировать при нескольких запросах и даже в разных компонентах. Это позволит легко кэшировать дорогостоящие запросы к базе данных!

use Livewire\Attributes\Computed;

// Кэширование по всем запросам
#[Computed(persist: true)]
public function user()
{
return User::findOrFail($this->userId);
}

// Кэш для всех экземпляров этого компонента
#[Computed(cache: true)]
public function user()
{
return User::findOrFail($this->userId);
}

#[Rule]

Новый атрибут #[Rule] заменяет свойство $rules и метод rules из v2. Добавьте #[Rule(['required', 'max:150'])] к свойству вашего компонента, чтобы указать Livewire, как должно проверяться это свойство.

<?php

// ...
use Livewire\Attributes\Rule;

class InvoiceItem extends Component
{
#[Rule(['required', 'max:120'])]
public $itemName;

// ...
}

#[Locked]

Новый атрибут #[Locked] позволяет запретить пользователю обновлять свойство во фронтенде. Это можно использовать в качестве меры безопасности для свойств, которые пользователи не должны иметь возможности изменять.

<?php

// ...
use Livewire\Attributes\Locked;

class InvoiceItem extends Component
{
#[Locked]
public $id;

// ...
}

Обновление

Процесс обновления для большинства приложений будет довольно быстрым. В бета-версию включена команда livewire:upgrade artisan, которая поможет справиться с некоторыми более трудоёмкими изменениями, а затем вы можете следовать руководству по обновлению, чтобы убедиться, что все обновления актуальны.

Если вы предпочитаете доверить обновление Livewire специалистам, мы с Калебом предлагаем услугу, которая поможет вам ускорить процесс обновления. Ознакомиться с ней можно здесь.

Новый дизайн сайта и домен

В дополнение ко всем новым функциям и улучшениям, сайт документации Livewire был полностью переработан, перестроен и переписан!

Сайт и документация Livewire получили новый дизайн

Новая документация гораздо более подробна, чем документация v2. В них подробно рассматривается каждая функция и подробно описывается, как Livewire работает с гидратацией, морфингом и вложенными компонентами. Я настоятельно рекомендую прочитать весь сайт, если у вас есть время. Это мастер-класс по созданию приложений Livewire v3!

Ещё одно важное объявление — сайт документации переносится на livewire.laravel.com. Перейдите туда и ознакомьтесь с ним!


Это длинное сообщение, но в нем так много новых возможностей! Я надеюсь, что вы попробуете v3 в ближайшее время и не стесняйтесь оставлять свои отзывы об улучшениях.

Дополнительные материалы

Предыдущая Статья

Node.js: В чём разница между exports и module.exports

Следующая Статья

Готовимся к переходу на Symfony 6.4 и Symfony 7.0