Laravel Livewire 3 em 2026: Aplicações Reativas e Perguntas de Entrevista

Domine Laravel Livewire 3 com este guia completo sobre componentes reativos, integração com Alpine.js, validação em tempo real e perguntas de entrevista técnica.

Laravel Livewire 3: componentes reativos e perguntas de entrevista técnica

Laravel Livewire 3 transforma a maneira como desenvolvedores PHP constroem interfaces dinâmicas e reativas sem escrever JavaScript. Esta evolução significativa do framework traz integração nativa com Alpine.js, performance otimizada através de morphing DOM, e uma sintaxe de componentes mais limpa que torna o desenvolvimento de aplicações em tempo real natural dentro do ecossistema Laravel.

Mudança Chave no Livewire 3

Livewire 3 utiliza Alpine.js internamente e requer as diretivas @livewireStyles e @livewireScripts no layout. A diretiva wire:model agora usa binding diferido por padrão — utilize wire:model.live para atualizações em tempo real.

Arquitetura do Livewire 3 e Binding Reativo

Livewire opera através de um conceito simples porém poderoso: componentes PHP que se re-renderizam no servidor e enviam diferenças HTML de volta ao navegador. Cada componente mantém seu estado entre requisições através de um mecanismo de serialização que preserva os valores das propriedades.

A arquitetura difere fundamentalmente da reatividade baseada em JavaScript. Em vez de manter um DOM virtual no cliente, Livewire serializa o estado do componente, envia com cada requisição, e realiza morphing do DOM baseado na resposta do servidor. Esta abordagem mantém a lógica de negócio em PHP enquanto entrega a experiência reativa que os usuários esperam.

app/Livewire/SearchUsers.phpphp
<?php

namespace App\Livewire;

use App\Models\User;
use Livewire\Component;
use Livewire\WithPagination;
use Livewire\Attributes\Url;

class SearchUsers extends Component
{
    use WithPagination;

    #[Url] // Sincroniza a propriedade com parâmetros de URL
    public string $search = '';

    #[Url]
    public string $role = '';

    public function updatedSearch(): void
    {
        // Reinicia a paginação quando a busca muda
        $this->resetPage();
    }

    public function render()
    {
        return view('livewire.search-users', [
            'users' => User::query()
                ->when($this->search, fn($q) => $q->where('name', 'like', "%{$this->search}%"))
                ->when($this->role, fn($q) => $q->where('role', $this->role))
                ->paginate(10),
        ]);
    }
}

O atributo #[Url] sincroniza automaticamente as propriedades com os parâmetros de consulta da URL, habilitando visualizações filtradas compartilháveis sem código adicional. O hook de ciclo de vida updatedSearch é acionado quando a propriedade search muda, reiniciando a paginação para a primeira página.

Sintaxe de Componentes Livewire 3 com Atributos PHP 8

Livewire 3 adota os atributos PHP 8 para configuração, substituindo a abordagem anterior baseada em métodos. Este estilo declarativo melhora a legibilidade e reduz código repetitivo nos componentes.

app/Livewire/CreatePost.phpphp
<?php

namespace App\Livewire;

use App\Models\Post;
use Livewire\Component;
use Livewire\Attributes\Rule;
use Livewire\Attributes\Locked;
use Livewire\Attributes\Computed;

class CreatePost extends Component
{
    #[Locked] // Não pode ser modificado pelo frontend
    public int $userId;

    #[Rule('required|min:5|max:200')]
    public string $title = '';

    #[Rule('required|min:50')]
    public string $content = '';

    #[Rule('nullable|array')]
    public array $tags = [];

    public function mount(): void
    {
        $this->userId = auth()->id();
    }

    #[Computed]
    public function wordCount(): int
    {
        return str_word_count($this->content);
    }

    public function save(): void
    {
        $validated = $this->validate();

        Post::create([
            'user_id' => $this->userId,
            ...$validated,
        ]);

        $this->reset(['title', 'content', 'tags']);
        $this->dispatch('post-created');
    }

    public function render()
    {
        return view('livewire.create-post');
    }
}

O atributo #[Locked] previne a manipulação do frontend de propriedades sensíveis — crítico para segurança ao passar IDs ou permissões. Os atributos #[Rule] definem validação inline, enquanto #[Computed] armazena em cache cálculos custosos até que as dependências mudem.

Consideração de Segurança

Sempre utilize #[Locked] para IDs de usuário, flags de permissão, e qualquer dado que não deva ser adulterado pelo navegador. Livewire transmite o estado do componente em cada requisição, tornando propriedades não protegidas vulneráveis a modificação.

Validação de Formulário em Tempo Real e Feedback ao Usuário

Livewire se destaca em fornecer feedback de validação imediato sem recarregamento de página. O framework valida propriedades enquanto os usuários digitam, exibindo erros ao lado dos campos do formulário em tempo real.

app/Livewire/RegistrationForm.phpphp
<?php

namespace App\Livewire;

use App\Models\User;
use Livewire\Component;
use Livewire\Attributes\Rule;
use Livewire\Attributes\Validate;
use Illuminate\Validation\Rules\Password;

class RegistrationForm extends Component
{
    #[Rule('required|email|unique:users,email')]
    public string $email = '';

    #[Rule('required|min:2|max:50')]
    public string $name = '';

    public string $password = '';
    public string $passwordConfirmation = '';

    public function rules(): array
    {
        return [
            'password' => [
                'required',
                'confirmed',
                Password::min(8)->mixedCase()->numbers(),
            ],
        ];
    }

    public function updated(string $property): void
    {
        // Valida uma única propriedade ao mudar
        $this->validateOnly($property);
    }

    public function register(): void
    {
        $validated = $this->validate();

        $user = User::create([
            'email' => $validated['email'],
            'name' => $validated['name'],
            'password' => bcrypt($validated['password']),
        ]);

        auth()->login($user);
        $this->redirect('/dashboard');
    }

    public function render()
    {
        return view('livewire.registration-form');
    }
}

O template Blade correspondente utiliza wire:model.blur para acionar a validação quando os usuários saem de um campo, equilibrando responsividade com carga do servidor:

blade
{{-- resources/views/livewire/registration-form.blade.php --}}
<form wire:submit="register" class="space-y-4">
    <div>
        <label for="email" class="block text-sm font-medium">E-mail</label>
        <input 
            wire:model.blur="email" 
            type="email" 
            id="email"
            class="mt-1 block w-full rounded-md border-gray-300"
        >
        @error('email')
            <p class="mt-1 text-sm text-red-600">{{ $message }}</p>
        @enderror
    </div>

    <div>
        <label for="password" class="block text-sm font-medium">Senha</label>
        <input 
            wire:model.blur="password" 
            type="password" 
            id="password"
            class="mt-1 block w-full rounded-md border-gray-300"
        >
        @error('password')
            <p class="mt-1 text-sm text-red-600">{{ $message }}</p>
        @enderror
    </div>

    <div>
        <label for="passwordConfirmation" class="block text-sm font-medium">Confirmar Senha</label>
        <input 
            wire:model.blur="passwordConfirmation" 
            type="password" 
            id="passwordConfirmation"
            class="mt-1 block w-full rounded-md border-gray-300"
        >
    </div>

    <button 
        type="submit" 
        class="w-full bg-indigo-600 text-white py-2 px-4 rounded-md"
        wire:loading.attr="disabled"
    >
        <span wire:loading.remove>Cadastrar</span>
        <span wire:loading>Processando...</span>
    </button>
</form>

Este padrão demonstra os pontos fortes do Livewire: lógica de validação do lado do servidor com responsividade do lado do cliente, estados de carregamento sem JavaScript, e separação limpa entre a lógica do componente e a apresentação.

Comunicação Pai-Filho com Eventos

Interfaces complexas frequentemente requerem múltiplos componentes Livewire que se comunicam entre si. Livewire 3 fornece um sistema de eventos refinado para este propósito, utilizando o padrão dispatch e o atributo #[On].

app/Livewire/TaskBoard.php - Componente paiphp
<?php

namespace App\Livewire;

use App\Models\Task;
use Livewire\Component;
use Livewire\Attributes\On;

class TaskBoard extends Component
{
    public array $columns = ['todo', 'in_progress', 'done'];

    #[On('task-moved')]
    public function handleTaskMoved(int $taskId, string $newStatus): void
    {
        Task::where('id', $taskId)->update(['status' => $newStatus]);
    }

    #[On('task-created')]
    public function refreshBoard(): void
    {
        // O componente re-renderiza automaticamente
    }

    public function render()
    {
        return view('livewire.task-board', [
            'tasks' => Task::all()->groupBy('status'),
        ]);
    }
}
app/Livewire/TaskCard.php - Componente filhophp
<?php

namespace App\Livewire;

use App\Models\Task;
use Livewire\Component;

class TaskCard extends Component
{
    public Task $task;

    public function moveToStatus(string $status): void
    {
        $this->dispatch('task-moved', taskId: $this->task->id, newStatus: $status);
    }

    public function render()
    {
        return view('livewire.task-card');
    }
}

Os eventos sobem pela hierarquia de componentes por padrão. Para comunicação entre componentes fora da relação pai-filho, os eventos podem direcionar componentes específicos usando $this->dispatch('event-name')->to(ComponentClass::class).

Pronto para mandar bem nas entrevistas de Laravel?

Pratique com nossos simuladores interativos, flashcards e testes tecnicos.

Integração Alpine.js para Interatividade do Lado do Cliente

Livewire 3 vem com Alpine.js integrado, habilitando padrões híbridos onde algumas interações acontecem do lado do cliente enquanto outras fazem a viagem de ida e volta ao servidor. Esta combinação reduz a carga do servidor para simples toggles de UI enquanto preserva o modelo do lado do servidor do Livewire para lógica complexa.

blade
{{-- resources/views/livewire/notification-dropdown.blade.php --}}
<div 
    x-data="{ 
        open: false,
        unreadCount: @entangle('unreadCount')
    }"
    class="relative"
>
    <button @click="open = !open" class="relative p-2">
        <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" 
                  d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6 6 0 10-12 0v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
        </svg>
        <span 
            x-show="unreadCount > 0"
            x-text="unreadCount"
            class="absolute -top-1 -right-1 bg-red-500 text-white text-xs rounded-full w-5 h-5 flex items-center justify-center"
        ></span>
    </button>

    <div 
        x-show="open" 
        @click.outside="open = false"
        x-transition
        class="absolute right-0 mt-2 w-80 bg-white rounded-lg shadow-lg"
    >
        @foreach($notifications as $notification)
            <div 
                wire:click="markAsRead({{ $notification->id }})"
                class="p-4 border-b hover:bg-gray-50 cursor-pointer"
            >
                <p class="font-medium">{{ $notification->title }}</p>
                <p class="text-sm text-gray-600">{{ $notification->message }}</p>
            </div>
        @endforeach
    </div>
</div>

A diretiva @entangle cria um binding bidirecional entre o estado do Alpine.js e as propriedades do Livewire. Mudanças em unreadCount no Alpine ou Livewire sincronizam automaticamente, habilitando atualizações otimistas da UI enquanto mantém a autoridade do servidor.

Upload de Arquivos com Acompanhamento de Progresso

Livewire fornece tratamento de upload de arquivos com acompanhamento de progresso integrado, validação e armazenamento temporário. O trait WithFileUploads adiciona toda a funcionalidade necessária a qualquer componente.

app/Livewire/DocumentUploader.phpphp
<?php

namespace App\Livewire;

use Livewire\Component;
use Livewire\WithFileUploads;
use Livewire\Attributes\Rule;
use Illuminate\Support\Facades\Storage;

class DocumentUploader extends Component
{
    use WithFileUploads;

    #[Rule('required|file|mimes:pdf,docx|max:10240')] // 10MB máximo
    public $document;

    public ?string $uploadedPath = null;

    public function updatedDocument(): void
    {
        $this->validate();
    }

    public function save(): void
    {
        $this->validate();

        $this->uploadedPath = $this->document->store('documents', 'public');

        $this->dispatch('document-uploaded', path: $this->uploadedPath);
        $this->reset('document');
    }

    public function render()
    {
        return view('livewire.document-uploader');
    }
}
blade
{{-- resources/views/livewire/document-uploader.blade.php --}}
<div class="space-y-4">
    <div 
        x-data="{ uploading: false, progress: 0 }"
        x-on:livewire-upload-start="uploading = true"
        x-on:livewire-upload-finish="uploading = false; progress = 0"
        x-on:livewire-upload-error="uploading = false"
        x-on:livewire-upload-progress="progress = $event.detail.progress"
    >
        <input 
            type="file" 
            wire:model="document"
            accept=".pdf,.docx"
            class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:bg-indigo-50 file:text-indigo-700"
        >

        <div x-show="uploading" class="mt-2">
            <div class="bg-gray-200 rounded-full h-2">
                <div 
                    class="bg-indigo-600 h-2 rounded-full transition-all duration-300"
                    :style="'width: ' + progress + '%'"
                ></div>
            </div>
            <p class="text-sm text-gray-600 mt-1" x-text="'Enviando: ' + progress + '%'"></p>
        </div>
    </div>

    @error('document')
        <p class="text-sm text-red-600">{{ $message }}</p>
    @enderror

    @if($document)
        <div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
            <span class="text-sm">{{ $document->getClientOriginalName() }}</span>
            <button wire:click="save" class="bg-indigo-600 text-white px-4 py-2 rounded-md text-sm">
                Enviar
            </button>
        </div>
    @endif
</div>

O template combina Alpine.js para visualização de progresso do lado do cliente com os eventos de upload do Livewire. Os arquivos são enviados primeiro para armazenamento temporário, permitindo pré-visualização e validação antes do armazenamento final.

Perguntas de Entrevista para Desenvolvedores Livewire 3

Entrevistas técnicas frequentemente exploram o entendimento da arquitetura do Livewire, seu modelo de segurança e características de performance. As seguintes perguntas aparecem frequentemente ao contratar desenvolvedores Laravel com experiência em Livewire.

Áreas de Foco em Entrevistas

Os entrevistadores avaliam três áreas principais: compreensão do ciclo de vida de requisições do Livewire, consciência de segurança em torno de propriedades públicas, e habilidade de otimizar performance de componentes. Demonstrar conhecimento de quando NÃO usar Livewire importa tanto quanto saber como usá-lo.

Como o Livewire mantém o estado do componente entre requisições?

Livewire serializa todas as propriedades públicas em um payload que viaja com cada requisição. O estado do componente é codificado, assinado para prevenir adulteração, e enviado ao navegador. Em interações subsequentes, este payload retorna ao servidor onde Livewire reconstrói a instância do componente antes de processar a ação.

Quais preocupações de segurança existem com propriedades públicas do Livewire, e como o Livewire as aborda?

Propriedades públicas são vulneráveis porque estão incluídas no payload serializado enviado ao navegador. Usuários maliciosos poderiam modificar este payload antes de enviá-lo de volta. Livewire aborda isso através de verificação de checksum — qualquer adulteração causa falha na requisição. Adicionalmente, o atributo #[Locked] marca propriedades como somente do lado do servidor, prevenindo que sejam modificadas via frontend mesmo se os checksums fossem comprometidos.

Explique a diferença entre wire:model, wire:model.live e wire:model.blur.

No Livewire 3, wire:model usa binding diferido por padrão — a propriedade só atualiza quando uma ação é disparada (como envio de formulário). wire:model.live atualiza a propriedade a cada mudança de entrada, disparando uma viagem de ida e volta ao servidor a cada tecla. wire:model.blur atualiza quando o elemento perde o foco, equilibrando responsividade com carga reduzida do servidor. O modificador .debounce adiciona atraso às atualizações ao vivo: wire:model.live.debounce.500ms.

Quando um desenvolvedor deveria escolher Livewire ao invés de Inertia.js ou um framework JavaScript?

Livewire se encaixa melhor quando a equipe tem forte experiência em PHP mas experiência limitada em JavaScript, quando requisitos de SEO favorecem conteúdo renderizado do lado do servidor, ou quando a aplicação envolve formulários complexos com validação do lado do servidor. Inertia.js ou frameworks JavaScript se adaptam melhor a dashboards altamente interativos com estado complexo do lado do cliente, aplicações que requerem funcionalidade offline, ou equipes com experiência em JavaScript que preferem o modelo mental de SPA.

Como otimizar componentes Livewire para performance?

As otimizações chave incluem: usar wire:model.blur ao invés de wire:model.live para reduzir requisições, implementar #[Computed] para cálculos custosos para habilitar cache, carregar componentes de forma lazy com wire:init para performance de renderização inicial, usar paginação para limitar dados enviados por requisição, e evitar re-renderizações desnecessárias delimitando propriedades reativas. Para listas, o atributo wire:key ajuda o Livewire a rastrear itens eficientemente durante o morphing.

Testando Componentes Livewire Efetivamente

Livewire fornece uma API de testes fluente que se integra com o framework de testes do Laravel. Os testes podem simular interações de usuário, verificar estado do componente, e verificar saída DOM.

tests/Feature/Livewire/SearchUsersTest.phpphp
<?php

namespace Tests\Feature\Livewire;

use App\Livewire\SearchUsers;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Tests\TestCase;

class SearchUsersTest extends TestCase
{
    use RefreshDatabase;

    public function test_can_search_users_by_name(): void
    {
        User::factory()->create(['name' => 'John Doe', 'role' => 'admin']);
        User::factory()->create(['name' => 'Jane Smith', 'role' => 'user']);

        Livewire::test(SearchUsers::class)
            ->set('search', 'John')
            ->assertSee('John Doe')
            ->assertDontSee('Jane Smith');
    }

    public function test_can_filter_by_role(): void
    {
        User::factory()->create(['name' => 'Admin User', 'role' => 'admin']);
        User::factory()->create(['name' => 'Regular User', 'role' => 'user']);

        Livewire::test(SearchUsers::class)
            ->set('role', 'admin')
            ->assertSee('Admin User')
            ->assertDontSee('Regular User');
    }

    public function test_pagination_resets_on_search_change(): void
    {
        User::factory()->count(25)->create();

        Livewire::test(SearchUsers::class)
            ->call('gotoPage', 2)
            ->assertSet('paginators.page', 2)
            ->set('search', 'test')
            ->assertSet('paginators.page', 1);
    }

    public function test_url_reflects_search_parameters(): void
    {
        Livewire::withQueryParams(['search' => 'existing'])
            ->test(SearchUsers::class)
            ->assertSet('search', 'existing');
    }
}

A suíte de testes cobre funcionalidade, gerenciamento de estado e sincronização de URL.

Conclusão

  • Livewire 3 habilita interfaces reativas em PHP puro através de renderização do lado do servidor e morphing DOM, eliminando a necessidade de frameworks JavaScript separados em muitas aplicações Laravel
  • Os atributos PHP 8 (#[Rule], #[Locked], #[Computed], #[Url]) fornecem configuração declarativa que melhora a legibilidade do código e a segurança
  • A integração integrada do Alpine.js permite padrões híbridos onde interações simples permanecem do lado do cliente enquanto lógica complexa permanece no servidor
  • A segurança requer atenção: usar #[Locked] para propriedades sensíveis e entender que propriedades públicas viajam em cada payload de requisição
  • A otimização de performance se concentra em reduzir viagens de ida e volta através de modificadores apropriados de wire:model e implementar propriedades computadas para operações custosas
  • As perguntas de entrevista focam em compreensão de arquitetura, consciência de segurança, e saber quando abordagens alternativas (Inertia.js, SPAs) se adaptam melhor aos requisitos

Comece a praticar!

Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.

Tags

#laravel
#livewire
#php
#reactive
#alpine.js

Compartilhar

Artigos relacionados