> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# ⚡ Livewire 4 入門 — 不需要 JavaScript 就能打造響應式 UI

> 使用 Laravel First-party 套件 Livewire 4，不需寫 JavaScript 就能建構互動式 UI 的方式。從安裝到實務程式碼皆有解說。

## 什麼是 Livewire

過去要在 Laravel 中做動態 UI，通常得結合 Vue.js 或 React 等 JavaScript 框架，或自己寫 AJAX 請求。

**Livewire** 是為此而生的 Laravel 套件。只用 PHP 與 Blade 就能建構響應式 UI。表單即時驗證、即時搜尋、計數器等傳統上需 JavaScript 的功能，全部可以用 PHP 實作。

<Info>
  Livewire 4 需要 Laravel 10 以上、PHP 8.1 以上。目前最新版本為 Livewire 4。
</Info>

### 運作機制概觀

Livewire 元件是伺服器端 PHP 類別與 Blade 模板的組合。使用者點擊按鈕或在表單輸入時，Livewire 在幕後送出 AJAX 請求執行 PHP，只把變更的部分反映到頁面。開發者可以完全不用意識這個機制，只寫 PHP。

***

## 安裝

在 Laravel 應用根目錄執行以下指令。

```shell theme={null}
composer require livewire/livewire
```

Laravel 的自動套件探索已啟用，因此不需要額外設定。

### 建立版面檔

作為全頁元件使用時需要版面檔。可用以下 Artisan 指令產生。

```shell theme={null}
php artisan livewire:layout
```

會產生 `resources/views/layouts/app.blade.php`。

```blade theme={null}
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <title>{{ $title ?? config('app.name') }}</title>

        @vite(['resources/css/app.css', 'resources/js/app.js'])

        @livewireStyles
    </head>
    <body>
        {{ $slot }}

        @livewireScripts
    </body>
</html>
```

`@livewireStyles` 與 `@livewireScripts` 會自動載入 Livewire 與 Alpine.js 的資源。

***

## 第一個元件

有一個 Artisan 指令可用來產生 Livewire 元件。我們來做個簡單的計數器。

```shell theme={null}
php artisan make:livewire Counter
```

此指令會產生 `resources/views/components/⚡counter.blade.php` 單檔元件。

<Tip>
  檔名的 ⚡ 用來一眼識別 Livewire 元件，提升在編輯器內的辨識度。若不需要可透過設定關閉。
</Tip>

將產生的檔案編輯為：

```blade theme={null}
<?php

use Livewire\Component;

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

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

    public function decrement(): void
    {
        $this->count--;
    }
};
?>

<div>
    <h1>計數：{{ $count }}</h1>

    <button wire:click="increment">+1</button>
    <button wire:click="decrement">-1</button>
</div>
```

要在 Blade 模板嵌入此元件，使用一般 Blade 元件語法。

```blade theme={null}
<livewire:counter />
```

每次點按鈕都會在不重新整理下更新計數。`wire:click` 讓 PHP 方法取代 JavaScript 被呼叫。

***

## 屬性與動作

### 屬性 — `wire:model`

`wire:model` 指令能雙向綁定輸入元素與元件屬性。

```blade theme={null}
<?php

use Livewire\Component;

new class extends Component {
    public string $name = '';
    public string $email = '';
};
?>

<div>
    <input type="text" wire:model="name" placeholder="姓名">
    <input type="email" wire:model="email" placeholder="Email">

    <p>你好，{{ $name }} ({{ $email }})</p>
</div>
```

預設 `wire:model` 只有在動作被執行（例如表單送出）時才與伺服器同步。若想每次輸入都同步，加上 `.live` 修飾詞。

| 寫法                     | 行為                   |
| ---------------------- | -------------------- |
| `wire:model`           | 動作執行時（例如表單送出）才同步（預設） |
| `wire:model.live`      | 每次輸入都送請求             |
| `wire:model.blur`      | 失去焦點時同步（不送請求）        |
| `wire:model.live.blur` | 失去焦點時送請求             |

### 動作 — `wire:click`、`wire:submit`

`wire:click` 綁定 click 事件、`wire:submit` 綁定表單送出事件。

```blade theme={null}
<?php

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

new class extends Component {
    public string $taskName = '';

    public function addTask(): void
    {
        Task::create(['name' => $this->taskName]);
        $this->taskName = '';
    }

    public function render()
    {
        return $this->view([
            'tasks' => Task::latest()->get(),
        ]);
    }
};
?>

<div>
    <form wire:submit="addTask">
        <input type="text" wire:model="taskName" placeholder="任務名稱">
        <button type="submit">加入</button>
    </form>

    <ul>
        @foreach ($tasks as $task)
            <li>{{ $task->name }}</li>
        @endforeach
    </ul>
</div>
```

***

## 即時驗證

Livewire 4 可用 `#[Validate]` 屬性直接在屬性上定義驗證規則。

```blade theme={null}
<?php

use Livewire\Attributes\Validate;
use Livewire\Component;
use App\Models\Post;

new class extends Component {
    #[Validate('required|min:3')]
    public string $title = '';

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

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

        Post::create([
            'title' => $this->title,
            'content' => $this->content,
        ]);

        $this->reset(['title', 'content']);

        session()->flash('message', '文章已儲存。');
    }
};
?>

<div>
    @if (session('message'))
        <div>{{ session('message') }}</div>
    @endif

    <form wire:submit="save">
        <div>
            <input type="text" wire:model.live.blur="title" placeholder="標題">
            @error('title') <span style="color: red;">{{ $message }}</span> @enderror
        </div>

        <div>
            <textarea wire:model.live.blur="content" placeholder="內容"></textarea>
            @error('content') <span style="color: red;">{{ $message }}</span> @enderror
        </div>

        <button type="submit">儲存</button>
    </form>
</div>
```

加了 `#[Validate]` 的屬性會在每次更新時自動驗證。搭配 `wire:model.live.blur` 就能達到「失去焦點瞬間顯示錯誤訊息」的即時驗證體驗。

<Info>
  `$this->validate()` 會在表單送出時把所有屬性一次驗證。搭配 `#[Validate]` 的自動驗證做成兩階段是推薦模式。
</Info>

***

## 生命週期 hook

Livewire 元件有多個生命週期 hook。

| Hook                          | 時機                 |
| ----------------------------- | ------------------ |
| `mount()`                     | 元件首次生成時（只 1 次）     |
| `boot()`                      | 每個請求的開始（初次與後續請求都會） |
| `updating($property, $value)` | 屬性即將更新前            |
| `updated($property)`          | 屬性更新後立即            |
| `rendering()`                 | view 繪製前           |
| `rendered()`                  | view 繪製後           |
| `dehydrate()`                 | 每個請求結束時            |

### `mount()` — 初始化

`mount()` 用於元件初始化，作為建構子的替代。

```blade theme={null}
<?php

use Illuminate\Support\Facades\Auth;
use Livewire\Component;

new class extends Component {
    public string $name = '';
    public string $email = '';

    public function mount(): void
    {
        $this->name = Auth::user()->name;
        $this->email = Auth::user()->email;
    }
};
?>

<div>
    <p>姓名：{{ $name }}</p>
    <p>Email：{{ $email }}</p>
</div>
```

### `updated()` — 屬性變更後處理

若要在屬性更新後插入處理，可使用 `updated()`。若要限定特定屬性，將名稱含入方法名。

```blade theme={null}
<?php

use Livewire\Component;

new class extends Component {
    public string $username = '';

    public function updatedUsername(): void
    {
        $this->username = strtolower($this->username);
    }
};
?>

<div>
    <input type="text" wire:model.live="username">
    <p>使用者名稱：{{ $username }}</p>
</div>
```

每次輸入都會自動轉為小寫。

***

## 與 Laravel 整合

### 活用 Eloquent 模型

Livewire 屬性可直接持有 Eloquent 模型。

```blade theme={null}
<?php

use Livewire\Attributes\Validate;
use Livewire\Component;
use App\Models\User;

new class extends Component {
    public User $user;

    public function mount(User $user): void
    {
        $this->user = $user;
    }

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

    public function save(): void
    {
        $this->validate();
        $this->user->update(['name' => $this->name]);
        session()->flash('message', '個人資料已更新。');
    }

    public function render()
    {
        return $this->view();
    }
};
?>

<div>
    @if (session('message'))
        <div>{{ session('message') }}</div>
    @endif

    <form wire:submit="save">
        <input type="text" wire:model="name" placeholder="姓名">
        @error('name') <span style="color: red;">{{ $message }}</span> @enderror
        <button type="submit">更新</button>
    </form>
</div>
```

### 表單物件

複雜表單可抽成 Form 物件，讓元件保持簡潔。

```shell theme={null}
php artisan livewire:form PostForm
```

```php theme={null}
namespace App\Livewire\Forms;

use Livewire\Attributes\Validate;
use Livewire\Form;
use App\Models\Post;

class PostForm extends Form
{
    #[Validate('required|min:3')]
    public string $title = '';

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

    public function store(): void
    {
        Post::create($this->only(['title', 'content']));
    }
}
```

從元件使用表單物件。

```blade theme={null}
<?php

use App\Livewire\Forms\PostForm;
use Livewire\Component;

new class extends Component {
    public PostForm $form;

    public function save(): void
    {
        $this->form->validate();
        $this->form->store();
        $this->form->reset();
        session()->flash('message', '貼文已建立。');
    }
};
?>

<div>
    <form wire:submit="save">
        <input type="text" wire:model="form.title" placeholder="標題">
        @error('form.title') <span style="color: red;">{{ $message }}</span> @enderror

        <textarea wire:model="form.content" placeholder="內容"></textarea>
        @error('form.content') <span style="color: red;">{{ $message }}</span> @enderror

        <button type="submit">送出</button>
    </form>
</div>
```

***

## 總結

以下整理 Livewire 特別適合的情境。

| 情境          | Livewire 能達成的事        |
| ----------- | --------------------- |
| 表單處理        | 即時驗證、錯誤顯示             |
| 資料列表        | 即時搜尋、排序、分頁            |
| 計數器或 toggle | 不重新載入的 UI 更新          |
| 管理介面        | 直接與 Eloquent 連動的 CRUD |
| 精靈式表單       | 用 PHP 實作步驟管理          |

只要「用過 Blade」的 Laravel 工程師，今天就能開始使用 Livewire。最大的優勢是不需 JavaScript 框架學習成本就能實現互動式 UI。

需要 SPA 級的高複雜度互動時 Inertia.js 較適合；但表單、管理介面、資料表這類用途，Livewire 通常實作起來更簡潔。不妨從一個小元件開始試試。

<Card title="Livewire 官方文件" icon="book-open" href="https://livewire.laravel.com/docs">
  Livewire 的完整功能（檔案上傳、分頁、測試等）請參考官方文件。
</Card>


## Related topics

- [開始學習 Laravel 前需要具備的知識](/zh-TW/true-tutorial.md)
- [前端](/zh-TW/frontend.md)
- [Vue.js 入門 — 搭配 Inertia × Laravel 使用的基礎知識](/zh-TW/blog/vue-introduction.md)
- [認證入門](/zh-TW/authentication.md)
- [Svelte 入門 — 搭配 Inertia × Laravel 使用的基礎知識](/zh-TW/blog/svelte-introduction.md)
