Livewire: интерактивность без JavaScript
Счётчик, живой поиск, форма с проверкой на лету обычно требуют JavaScript и отдельного API. Livewire позволяет написать всё это на PHP: компонент хранит состояние на сервере, а страница обновляется сама, без перезагрузки.
Установка
composer require livewire/livewire
php artisan make:livewire counterКоманда создаёт класс app/Livewire/Counter.php и шаблон resources/views/livewire/counter.blade.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">+1</button>
<span>{{ $count }}</span>
</div>Открытые свойства класса доступны в шаблоне. Щелчок по кнопке отправляет запрос на сервер, вызывает метод и обновляет разметку.
Вставка на страницу
<livewire:counter />Привязка поля
wire:model связывает поле формы со свойством:
class PostSearch extends Component
{
public string $query = '';
public function render()
{
return view('livewire.post-search', [
'posts' => Post::where('title', 'like', "%{$this->query}%")->limit(10)->get(),
]);
}
}<div>
<input type="search" wire:model.live.debounce.300ms="query" placeholder="Поиск">
<ul>
@foreach ($posts as $post)
<li wire:key="{{ $post->id }}">{{ $post->title }}</li>
@endforeach
</ul>
</div>Слово live обновляет компонент при вводе, debounce.300ms ждёт паузы в наборе, чтобы не отправлять запрос на каждую букву.
Форма с проверкой
use Livewire\Attributes\Validate;
class CreatePost extends Component
{
#[Validate('required|min:5')]
public string $title = '';
#[Validate('required')]
public string $body = '';
public function save()
{
$this->validate();
Post::create(['title' => $this->title, 'body' => $this->body, 'user_id' => auth()->id()]);
return $this->redirect('/posts');
}
public function render()
{
return view('livewire.create-post');
}
}<form wire:submit="save">
<input type="text" wire:model="title">
@error('title') <p class="error">{{ $message }}</p> @enderror
<textarea wire:model="body"></textarea>
@error('body') <p class="error">{{ $message }}</p> @enderror
<button type="submit">
<span wire:loading.remove>Сохранить</span>
<span wire:loading>Сохраняем…</span>
</button>
</form>wire:loading показывает элемент, пока идёт запрос.
Что выбрать
| Инструмент | Когда подходит |
|---|---|
| Blade и немного Alpine.js | Обычные страницы с парой интерактивных мелочей |
| Livewire | Интерактивные формы, фильтры, таблицы — без отдельного фронтенда |
| Inertia с React или Vue | Сложный интерфейс, команда с фронтенд-разработчиками |
Частая ошибка
Открытые свойства компонента передаются в браузер, и посетитель может изменить их перед отправкой. Нельзя доверять свойству вроде $userId или $price: проверяйте права и данные в каждом методе, как в обычном контроллере. Свойства, которые клиенту менять нельзя, помечают атрибутом #[Locked].