Livewire 3 und 4 — was sich geändert hat

Alles, was sich zwischen Livewire 3 und Livewire 4 geändert hat: die neuen Features, die sich lohnen, jeder Breaking Change mit Vorher/Nachher-Code und eine Checkliste für das Upgrade selbst. Einschließlich der drei Änderungen, die Ihre Testsuite bestehen und die Produktion lahmlegen.

Livewire 3 → 4 Breaking Changes Migrations-Checkliste 14 Abschnitte

1. Die Kurzfassung

Livewire 4 ist ein großes Release mit einem kleinen Upgrade-Pfad. Das Komponentenmodell hat sich nicht geändert: PHP-Klasse, Blade-Vorlage, Zustand auf dem Server, Markup über die Leitung. Geändert hat sich, wo Komponenten liegen, wie sie geschrieben werden und wie viel einer Seite ein einzelnes Update berührt.

Klassenbasierte Komponenten funktionieren weiter. Single-File ist der Standard für neue Komponenten, kein erzwungenes Neuschreiben. Die meisten Livewire-3-Anwendungen upgraden so: Version anheben, eine Handvoll Breaking Changes beheben und die neuen Features schrittweise übernehmen.

Was Sie gewinnen

  • Single-File-Komponenten — Klasse und Markup in einer Datei.
  • Islands — Bereiche, die sich unabhängig neu rendern, ohne Aufteilung in Kindkomponenten.
  • Slots — Eltern reichen Markup hinein, ausgewertet im Kontext des Elternteils.
  • Optimistisches UIwire:show, wire:text, wire:bind ändern das DOM ohne Round Trip.
  • Drag and Dropwire:sort, ohne externe Bibliothek.
  • Scoped CSS — Komponentenstile, die nicht auslaufen.

Was Sie erwischt

  1. Nicht geschlossene <livewire:…>-Tags rendern lautlos gar nichts.
  2. wire:model-Modifikatoren haben ihre Bedeutung geändert — Felder synchronisieren nicht mehr.
  3. Der Update-Endpunkt liegt jetzt unter /livewire-{hash}/ — Infrastrukturregeln brechen.
  4. wire:transition-Modifikatoren wurden entfernt.
  5. Volt ist in den Kern gewandert.

2. Gegenüberstellung

BereichLivewire 3Livewire 4
Komponentendateien app/Livewire/Counter.php + resources/views/livewire/counter.blade.php resources/views/components/⚡counter.blade.php (eine Datei)
Make-Befehl php artisan livewire:make Counter php artisan make:livewire counter
Routing Route::get('/x', Dashboard::class) Route::livewire('/x', 'pages::dashboard')
Partielles Rendern Aufteilung in Kindkomponenten @island-Bereiche
Slots Nicht unterstützt {{ $slot }}, <wire:slot name="…">
Drag and Drop SortableJS plus Klebecode wire:sort
Sofortiges UI Alpine.js von Hand wire:show, wire:text, wire:bind
Komponenten-CSS Globales Stylesheet Scoped <style> in der Komponente
Parallele Aktionen Nicht verfügbar #[Async], wire:click.async
Update-Endpunkt /livewire/update /livewire-{hash}/update
JS-Hooks Livewire.hook('request'|'commit') interceptRequest(), interceptMessage()
Volt Separates Paket Im Kern enthalten
Testing PHPUnit oder Pest Pest empfohlen, Tests dürfen neben der Komponente liegen

3. Das Upgrade Schritt für Schritt

Bash
# 1. Abhängigkeit anheben
composer require livewire/livewire:^4.0

# 2. Alle Caches leeren
php artisan optimize:clear

# 3. Testsuite laufen lassen, bevor Sie sonst etwas anfassen
php artisan test

Laravel Shift automatisiert einen großen Teil der mechanischen Arbeit. Ob Sie es nutzen oder nicht — arbeiten Sie danach die Checkliste unten durch; genau die lautlosen Fehler erkennt ein Werkzeug am schlechtesten.

Eine Reihenfolge, die meist glattgeht

  1. Paket aktualisieren und Caches leeren.
  2. Konfigurations-Umbenennungen beheben (Kapitel 4).
  3. Routing anpassen (Kapitel 5).
  4. Codebasis nach nicht geschlossenen Komponenten-Tags durchsuchen (Kapitel 6).
  5. Jedes wire:model mit Modifikator prüfen (Kapitel 7).
  6. Infrastrukturregeln für den neuen Endpunkt anpassen (Kapitel 8).
  7. Entfernte wire:transition-Modifikatoren ersetzen (Kapitel 9).
  8. Volt-Komponenten migrieren, falls im Einsatz (Kapitel 12).
  9. Testsuite erneut laufen lassen und die App im Browser durchklicken.
  10. Erst danach Islands, Slots und Single-File-Komponenten angehen.
Verbinden Sie das Upgrade nicht mit einem Umbau. Bringen Sie die Anwendung zuerst mit den vorhandenen klassenbasierten Komponenten auf v4 ins Grüne.

4. Umbenennungen in der Konfiguration

Livewire 3Livewire 4Hinweis
layoutcomponent_layoutNutzt den Namespace layouts::
lazy_placeholdercomponent_placeholder
smart_wire_keyssmart_wire_keysStandard von false auf true gewechselt
component_locationsVerzeichnisse, die nach Komponenten durchsucht werden
component_namespacesBenannte Wurzeln, etwa pages::
make_commandSingle-File- oder klassenbasierte Generierung
csp_safeBuild für strengere CSP
Livewire 4 — config/livewire.php
'component_layout' => 'layouts::app',

'component_placeholder' => 'livewire.placeholder',

'component_locations' => [
    'resources/views/components',
    'resources/views/livewire',
],

'component_namespaces' => [
    'pages' => 'resources/views/pages',
],

// Weiterhin klassenbasierte Komponenten erzeugen
'make_command' => [
    'type' => 'class',
],

5. Routing

Livewire 3
use App\Livewire\Dashboard;

Route::get('/dashboard', Dashboard::class)
    ->middleware('auth')
    ->name('dashboard');
Livewire 4
use App\Livewire\Dashboard;

Route::livewire('/dashboard', Dashboard::class)
    ->middleware('auth')
    ->name('dashboard');

// Oder nach Komponentenname
Route::livewire('/dashboard', 'pages::dashboard')
    ->middleware('auth')
    ->name('dashboard');
Schnell alle finden: grep -rn "Route::get(.*::class" routes/.

6. Komponenten-Tags müssen geschlossen sein

Die unangenehmste Änderung, weil sie lautlos scheitert. Ein nicht geschlossener Komponenten-Tag rendert gar nichts — keine Exception, kein Logeintrag, nur ein fehlender Block auf der Seite.
Livewire 3 — toleriert
<livewire:user-profile>
<livewire:order-list>
Livewire 4 — Pflicht
<livewire:user-profile />
<livewire:order-list />
Bash
# Komponenten-Tags, die nicht selbstschließend sind
grep -rn "<livewire:[^>]*[^/]>" resources/views/

# Die Direktivenform @livewire() ist nicht betroffen
grep -rn "@livewire(" resources/views/

7. wire:model hat sich zweimal geändert

7.1 Modifikatoren steuern die clientseitige Synchronisation

Livewire 3
<input wire:model.blur="title">
<select wire:model.change="status">
Livewire 4 — gleiches Verhalten wie zuvor
<input wire:model.live.blur="title">
<select wire:model.live.change="status">

7.2 Kein Bubbling von Kindelementen mehr

Livewire 3 — der Wrapper fing es ab
<div wire:model="value">
    <input type="text">
</div>
Livewire 4 — ausdrücklich aktivieren
<div wire:model.deep="value">
    <input type="text">
</div>
Bash
# Jedes wire:model mit Modifikator
grep -rn "wire:model\.[a-z]" resources/views/

# wire:model auf einem Nicht-Input-Element ist ein Bubbling-Kandidat
grep -rn "<div[^>]*wire:model" resources/views/
Eigene Formularkomponenten — Datepicker, Editor, gekapseltes Select — trifft die Bubbling-Änderung am häufigsten, weil das Wrapper-Muster genau ihre übliche Bauweise ist.

8. Der Update-Endpunkt ist umgezogen

Alle Livewire-URLs enthalten jetzt einen Hash: aus /livewire/ wurde /livewire-{hash}/.

Diese Änderung besteht jeden Test und legt die Produktion lahm. Nichts in Ihrem PHP-Code verweist auf den Pfad, also bleibt die Suite grün.

Wo Sie nachsehen sollten

  • location-Blöcke in nginx oder Apache
  • WAF- und Firewall-Allowlists
  • CDN-Regeln zum Cache-Bypass
  • Rate Limiter, die am Pfad hängen
  • Routing-Regeln des Load Balancers
  • Monitoring- und Uptime-Checks
  • connect-src-Einträge in der CSP
nginx — vorher
location /livewire/ {
    # …
}
nginx — nachher
location ~ ^/livewire(-[a-z0-9]+)?/ {
    # …
}

9. wire:transition wurde neu gebaut

Livewire 3
<div wire:transition.opacity.duration.300ms>
    …
</div>
Livewire 4
<div wire:transition>
    …
</div>
CSS
::view-transition-old(root),
::view-transition-new(root) {
    animation-duration: 300ms;
}
Die Browserunterstützung für View Transitions ist nicht flächendeckend. Wo sie fehlt, erscheint der Inhalt einfach ohne Animation.

10. Änderungen in der PHP-API

Streaming

Livewire 3
$this->stream(to: '#container', content: 'Hello', replace: true);
Livewire 4
$this->stream('Hello', replace: true, el: '#container');

Komponenten mounten

Livewire 3
mount($name, $params = [], $key = null)
Livewire 4
mount($name, $params = [], $key = null, $slots = [])

Neue Attribute

AttributZweck
#[Async]Aktion parallel ausführen
#[Renderless]Neurendern nach der Aktion überspringen
#[Prop]Eigenschaft als Prop vom Elternteil deklarieren
#[Json]Daten direkt an JavaScript zurückgeben

11. Änderungen in der JavaScript-API

Livewire 3
$wire.$js('showToast', { message: 'Gespeichert' });
Livewire 4
$wire.$js.showToast = { message: 'Gespeichert' };

Aus Hooks wurden Interceptors

Livewire 3
document.addEventListener('livewire:init', () => {
    Livewire.hook('request', ({ options, fail }) => {
        options.headers['X-Tenant'] = window.tenantId;

        fail(({ status, preventDefault }) => {
            if (status === 419) {
                preventDefault();
                window.location.reload();
            }
        });
    });
});
Livewire 4
document.addEventListener('livewire:init', () => {
    Livewire.interceptRequest(({ options, fail }) => {
        options.headers['X-Tenant'] = window.tenantId;

        fail(({ status, preventDefault }) => {
            if (status === 419) {
                preventDefault();
                window.location.reload();
            }
        });
    });

    Livewire.interceptMessage(({ component, succeed }) => {
        succeed(() => {
            console.debug('Komponente aktualisiert', component.name);
        });
    });
});
Blade
{{-- Validierungsfehler auf dem Client --}}
<div x-show="$errors.has('email')" x-text="$errors.first('email')"></div>

12. Volt ist in den Kern gewandert

VoltLivewire 4
Livewire\Volt\ComponentLivewire\Component
Volt::route()Route::livewire()
Volt::test()Livewire::test()
Volt Service ProviderEntfernen
Paket livewire/voltAbhängigkeit entfernen
Bash
composer remove livewire/volt
Vorher (Volt)
<?php

use Livewire\Volt\Component;

new class extends Component {
    public string $title = '';
};

?>

<div>…</div>
Nachher (Livewire 4)
<?php

use Livewire\Component;

new class extends Component {
    public string $title = '';
};

?>

<div>…</div>

13. Die neuen Features übernehmen

1. Islands auf Ihrer langsamsten Seite

Vorher — die ganze Komponente rendert neu
<div>
    <div class="card">Umsatz: {{ $this->revenue }}</div>
    <div class="card">Offene Jobs: {{ $this->queue }}</div>
    <div class="card">Letzte Aktivität: …</div>
</div>
Nachher — jede Karte aktualisiert für sich
<div>
    @island(name: 'revenue')
        <div class="card">Umsatz: {{ $this->revenue }}</div>
    @endisland

    @island(name: 'queue', poll: '5s')
        <div class="card">Offene Jobs: {{ $this->queue }}</div>
    @endisland

    @island(name: 'activity', lazy: true)
        @placeholder
            <div class="card skeleton animate-pulse h-48"></div>
        @endplaceholder

        <div class="card">Letzte Aktivität: …</div>
    @endisland
</div>

2. Round Trips durch Client-Direktiven ersetzen

Vorher
<button wire:click="$toggle('showFilters')">Filter</button>

@if ($showFilters)
    <div class="filters">…</div>
@endif
Nachher — gar keine Anfrage
<button wire:click="$toggle('showFilters')">Filter</button>

<div class="filters" wire:show="showFilters">…</div>

3. SortableJS entfernen

Nachher
<ul wire:sort="reorder">
    @foreach ($tasks as $task)
        <li wire:sort:item="{{ $task->id }}" wire:key="task-{{ $task->id }}">
            <span wire:sort:handle>⠿</span> {{ $task->title }}
        </li>
    @endforeach
</ul>

4. Kindkomponenten zusammenführen, die nur fürs Rendern existierten

Hat ein Kind keinen eigenen Zustand und wird nicht wiederverwendet, ist ein Island im Elternteil leichter — eine Komponente, eine Klasse, ein Snapshot.

5. #[Renderless] für unsichtbare Arbeit

PHP
use Livewire\Attributes\Renderless;

#[Renderless]
public function trackView(): void
{
    $this->post->increment('views');
}

6. Umstellung auf Single-File — zuletzt

Das ist Kosmetik. Machen Sie es beiläufig, wenn Sie die Datei ohnehin anfassen, nicht als eigenen Migrationssprint.

14. Upgrade-Checkliste

Vor dem Start

  • Die Testsuite läuft auf Livewire 3 grün.
  • Ein Branch und ein Datenbank-Backup, wenn die Umgebung geteilt wird.
  • Jemand, der die App danach durchklickt — die lautlosen Fehler sieht nur ein Mensch.

Mechanische Änderungen

  • composer require livewire/livewire:^4.0
  • php artisan optimize:clear
  • layoutcomponent_layout
  • lazy_placeholdercomponent_placeholder
  • Route::get(…, Component::class)Route::livewire(…)
  • Alle <livewire:…>-Tags selbstschließend
  • wire:model.blur / .change.live ergänzen
  • Wrapper-wire:model.deep ergänzen
  • wire:scrollwire:navigate:scroll
  • wire:transition-Modifikatoren → ins CSS
  • Argumentreihenfolge von $this->stream()
  • $wire.$js('name', …)$wire.$js.name = …
  • Livewire.hook(…)interceptRequest() / interceptMessage()
  • Volt-Klassen, -Routen, -Tests und -Paket entfernt

Infrastruktur

  • nginx-/Apache-Location-Blöcke auf /livewire-{hash}/ erweitert
  • WAF-, CDN- und Firewall-Regeln aktualisiert
  • Rate Limiter und Monitoring umgestellt
  • connect-src in der CSP geprüft

Verifikation

  • Testsuite grün
  • Alle Formulare senden und validieren
  • Datei-Uploads funktionieren, inklusive Fortschrittsanzeige
  • Tabellen paginieren, sortieren und filtern
  • Modals öffnen und schließen
  • Drittanbieter-Widgets in wire:ignore initialisieren weiterhin
  • Browser-Konsole sauber in den Hauptabläufen
  • Update-Anfragen liefern in der Produktion 200, nicht 403 von der WAF
Rollen Sie über einen Canary aus, wenn möglich. Beide Fehlerbilder, die eine grüne Testsuite überleben — nicht geschlossene Tags und der verschobene Endpunkt — zeigen sich unter echtem Traffic sofort und lassen sich trivial zurückrollen.

Offizielle Quellen

Livewire 4 entwickelt sich schnell weiter. Wo dieser Artikel und der offizielle Upgrade Guide auseinandergehen, ist der offizielle Guide maßgeblich.