Livewire позволяет строить реактивные интерфейсы на Laravel, не уходя в SPA. Компонент — это PHP-класс и Blade-шаблон; браузер отправляет запрос, сервер перерисовывает HTML, а библиотека точечно обновляет DOM. Ни API-слоя, ни дублирования валидации, ни отдельного фронтенд-проекта.

Как это работает

При первом рендере Livewire отдаёт HTML и снимок состояния компонента. Действие вроде wire:click отправляет POST на /livewire/update с этим снимком и именем метода. Сервер восстанавливает компонент, выполняет метод, рендерит шаблон заново и возвращает HTML вместе с новым снимком. Клиент применяет изменения через morph.

Отсюда два следствия. Состояние живёт на сервере, поэтому его не нужно синхронизировать вручную. И каждое действие — сетевой запрос, поэтому дорогие вычисления и N+1 в шаблоне бьют по отзывчивости сильнее, чем в SPA.

Установка

В Laravel 11 и новее Livewire ставится композером; сервис-провайдер регистрируется автоматически. Скрипты подключаются на страницу без ручной вставки тегов.

composer require livewire/livewire:^4.0
php artisan make:livewire Counter
php artisan route:list
php artisan about

Компонент появится в app/Livewire и resources/views/livewire. В четвёртой ветке по умолчанию доступны однофайловые компоненты, но классическая пара «класс + шаблон» полностью поддерживается и лучше подходит для постепенного внедрения.

Первый компонент

Публичные свойства — это состояние, которое видит браузер. Методы — действия. Шаблон возвращается из render.

<?php

namespace App\Livewire;

use Livewire\Component;

class Counter extends Component
{
    public int $count = 0;

    public function increment(): void
    {
        $this->count++;
    }

    public function render()
    {
        return view('livewire.counter');
    }
}
<div>
    <button wire:click="increment">Плюс</button>
    <span>{{ $count }}</span>
</div>

Дальше компонент вставляется тегом livewire:counter или директивой @livewire. Не указывайте в публичных свойствах объекты, которые нельзя сериализовать: модели сохраняются по идентификатору, а произвольные классы — нет.

Формы и валидация

Валидация выполняется на сервере теми же правилами Laravel. Атрибуты #[Validate] убирают дублирование, а wire:model с модификаторами управляет моментом отправки.

use Livewire\Attributes\Validate;

class SubscribeForm extends Component
{
    #[Validate('required|email|max:255')]
    public string $email = '';

    public function save(): void
    {
        $this->validate();
        Subscriber::create(['email' => $this->email]);
        session()->flash('ok', 'Готово');
        $this->reset('email');
    }
}

В шаблоне используйте wire:model.blur для проверки после ухода из поля и wire:model.live.debounce.400ms для живого поиска. Ошибки доступны через @error, а состояние загрузки — через wire:loading. Не отключайте серверную валидацию, даже если добавили клиентскую.

Списки и поиск

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

use Livewire\Attributes\Computed;
use Livewire\WithPagination;

class PostList extends Component
{
    use WithPagination;

    public string $q = '';

    #[Computed]
    public function posts()
    {
        return Post::query()
            ->when($this->q, fn ($query) => $query->where('title', 'like', "%{$this->q}%"))
            ->latest()
            ->paginate(15);
    }
}

В шаблоне обращайтесь к $this->posts и не забудьте wire:key в цикле. Ключ должен быть стабильным и уникальным, иначе morph перепутает элементы и состояние полей поедет. Для больших списков включайте ленивую загрузку и не рендерите сотни строк сразу.

Связь компонентов и файлы

Дочерний компонент не должен знать о родителе. Вместо этого он отправляет событие через $this->dispatch(‘post-created’), а родитель подписывается атрибутом #[On(‘post-created’)] и обновляет список. Такой обмен работает и между разными частями страницы.

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

Производительность

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

  • wire:key на каждом элементе списка.
  • #[Locked] для идентификаторов, которые пользователь не должен менять.
  • wire:poll с разумным интервалом вместо постоянного опроса.
  • Изолированные области и ленивые компоненты для тяжёлых блоков.
  • Пагинация вместо вывода всей таблицы.

В четвёртой ветке появились изолированные области: тяжёлый блок обновляется отдельно и не блокирует остальную страницу. Там же автоматически проставляется атрибут состояния загрузки, поэтому вместо сложных конструкций с wire:target достаточно стилизовать элемент по data-loading.

Для отладки смотрите вкладку Network: видно размер снимка, время ответа и имя вызванного метода. Если ответ вырос до сотен килобайт, почти всегда причина — коллекция моделей в публичном свойстве.

Тестирование

Компоненты тестируются без браузера: Livewire поднимает их в тесте и позволяет вызывать методы, проверять свойства и HTML.

Livewire::test(Counter::class)
    ->assertSet('count', 0)
    ->call('increment')
    ->assertSet('count', 1)
    ->assertSee('1');

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

Безопасность и типичные ошибки

Публичные свойства приходят от клиента и могут быть изменены. Никогда не доверяйте идентификатору из свойства, если он определяет доступ: загружайте запись с проверкой владельца. Для полей, которые нельзя менять, ставьте #[Locked].

Не выполняйте в действиях то, что должно происходить только по расписанию, и не передавайте в шаблон секреты. Файлы загружайте через встроенный механизм с валидацией типа и размера. Livewire не отменяет CSRF-защиту: токен подставляется автоматически, но кастомные fetch-запросы придётся оформлять вручную.

Итог

Livewire — самый короткий путь к интерактивному интерфейсу на Laravel без отдельного фронтенда: состояние на сервере, валидация в одном месте, обновление DOM по действию. Начните с одного компонента, измерьте число запросов и размер ответов, затем расширяйте.

Держите свойства компактными, ставьте wire:key, проверяйте права в действиях и используйте ленивую загрузку для тяжёлых блоков. Тогда серверный рендеринг остаётся простым и предсказуемым даже на нагруженном интерфейсе с десятками действий в минуту.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте