【Laravel實戰】15分鐘搞懂Livewire的組件渲染

【Laravel實戰】15分鐘搞懂Livewire的組件渲染

使用組件

如果想為你的 Blade 視圖加入Livewire組件,最簡單的方式就是使用<livewire:組件名稱>標籤,像是這樣:

<div>
    <livewire:show-posts />
</div>

或者也可以使用 Blade 的 @livewire 指令

@livewire('show-posts’)

這兩個寫法都是相同的,不過標籤的形式限定 Laravel 7 以上的版本,請特別注意。而Laravel針對7以下的版本也將不再維護,所以版本低於7的建議安排時間升級一下

假如你的 Livewire 組件是存放在 livewire 的子資料夾內,可以在名字前加上 一個點 "." 來說明其完整的命名空間為何,像這樣

<livewire:nav.show-posts />

傳入參數

為了讓組件更為彈性,比如變更模式或者開關功能等,你往往需要傳入額外參數到組件內來達到傳遞資料的目的,比如我們有一個 show-post 組件,能夠這樣寫來傳入 $post 模型

<livewire:show-post :post="$post”>

如果是 Blade 指令的版本,則可以第二參數來傳入,像是這樣寫

@livewire('show-post', ['post' => $post])

參數傳入之後,組件將要怎麼接收參數呢? 很簡單,Livewire將會自動將參數分配給同名的屬性,比如像剛才的例子,假如 show-post 組件有一個同樣名為 $post 的屬性,它將會被自動的賦值

class ShowPost extends Component
{
    public $post;

    ...
}

假如不幸因未知原因自動賦值失敗的話,沒關係,你還是可以透過 mount() 來自己完成賦值。要特別注意要存取公開屬性,必須要透過物件變數 $this

class ShowPost extends Component
{
    public $title;
    public $content;

    public function mount($post)
    {
        $this->title = $post->title;
        $this->content = $post->content;
    }

    ...
}

說到 mount() ,有件事你應該要知道。在 Livewire 組件中,你應該使用 mount() 而非建構子 __construct() 來完成屬性的初始化工作。 mount() 只會執行一次,就在組件被初始化的那一刻,順序會在 render() 呼叫前。就如控制器的方法一樣,你能夠利用型別提示的依賴注入技巧在參數上

use \Illuminate\Session\SessionManager;

class ShowPost extends Component
{
    public $title;
    public $content;

    public function mount(SessionManager $session, $post)
    {
        $session->put("post.{$post->id}.last_viewed", now());

        $this->title = $post->title;
        $this->content = $post->content;
    }

    ...
}

組件路由

假如整個頁面就是一個 Livewire 組件,也就是全頁組件,你就能夠在路由規則上直接傳入組件,將之當作控制器使用

//routes\web.php
Route::get('/post', ShowPosts::class);

預設 Livewire 將會渲染 ShowPosts 組件的內容到 {{ $slot }},也就是插槽,預設的佈局視圖為 resources/views/layouts/app.blade.php

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

    @livewireScripts
</body>

假如因為專案的需要,你想要改用其他視圖而非預設的佈局視圖,你能夠複寫 config/livewire 設定檔的 layout 選項 'layout' => ‘app.other_default_layout' 來達到,別忘了你需要發佈設定檔到你的專案內,詳情請參考 15分鐘搞懂 Livewire 的套件安裝與組件建立

而如果你希望動態調整而不透過設定,也就是在程式中去判斷要使用的視圖,你能夠在 render() 後面接著使用 layout()

class ShowPosts extends Component
{
    ...
    public function render()
    {
        return view('livewire.show-posts')
            ->layout('layouts.base');
    }
}

假如你使用的不是預設的組件插槽,你能夠接著呼叫 slot() 來指定插槽

public function render()
{
    return view('livewire.show-posts')
        ->layout('layouts.base')
        ->slot('main');
}

當然,Livewire 1.x 所使用的傳統 Blade layout語法還是管用的,你依然可以使用 @extends,假如你的父視圖透過 @yield('content') 來指定插入位置,像這樣:

<head>
    @livewireStyles
</head>
<body>
    @yield('content')

    @livewireScripts
</body>

你就需要把 layout() 改成使用 extends()

public function render()
{
    return view('livewire.show-posts')
        ->extends('layouts.app');
}

假如你需要指定組件所要渲染的 section 為何,一般原因是 section的名稱不是預設的content,你就能夠透過 section() 來加以指定

public function render()
{
    return view('livewire.show-posts')
        ->extends('layouts.app')
        ->section('body');
}

假如你需要從組件類別將資料傳入 layout ,也沒問題。你能夠在呼叫 layout() 時透過其第二參數來傳遞資料

public function render()
{
    return view('livewire.show-posts')
        ->layout('layouts.base', ['title' => 'Show Posts'])
}

取得路徑參數

一般你需要在控制器方法中去取用路徑參數,因為我們不再使用控制器, Livewire 會試著去模擬一個類似的行為,也就是透過 mount(),像這個例子:

//routes\web.php

Route::get('/post/{id}', ShowPost::class);
//app\Http\Livewire\ShowPost.php

class ShowPost extends Component
{
    public $post;

    public function mount($id)
    {
        $this->post = Post::find($id);
    }

    ...
}

你看, mount() 在組件中就扮演著類似控制器方法的角色,同樣可以取用路徑參數。比如你訪問 /post/123, $id 參數傳入 mount() 將會包含123的這個值

總結來說, mount() 的參數不但能夠取得使用組件時所傳入的參數,也能夠取得路徑參數,這裡請留意

Route Model Binding

Laravel 所自帶的 Route Model Binding ,讓我們能夠便利的取得該主鍵的對應資料,而這個功能在 Livewire 同樣適用,請看下面這個例子:

//routes\web.php

Route::get('/post/{post}', ShowPost::class);
//app\Http\Livewire\ShowPost.php

class ShowPost extends Component
{
    public $post;

    public function mount(Post $post)
    {
        $this->post = $post;
    }
}

假如你用的是 PHP 7.4 或者是以上版本,你還能夠針對類別屬性做型別提示, Livewire 也會自動地幫你進行 Route Model Binding。比如下面組件的 $post 屬性將會被自動注入,在 mount() 內不需要做任何事情,讓工作變得更為簡單

//app\Http\Livewire\ShowPost.php

class ShowPost extends Component
{
    public Post $post;
}

render()

Livewire 組件的 render() 會在頁面初次載入的時候被呼叫。假如是很簡單的組件,你就不需要自己定義 render(),基礎的 Livewire 根組件就包含了一個動態的 render()

一般而言,在 render() 內被預期要回傳一個 Blade 視圖,因此,你能夠將之比做是一個控制器方法,下面是一個例子:

class ShowPosts extends Component
{
    public function render()
    {
        return view('livewire.show-posts', [
            'posts' => Post::all(),
        ]);
    }
}
//resources/views/livewire/show-posts.blade.php

<div>
    @foreach ($posts as $post)
        @include('includes.post', $post)
    @endforeach
</div>

視圖的DOM部分,請特別注意的是請確認你的 Blade 視圖內只有一個根元素,以免出現前端程式錯誤的問題,比如分頁功能失效

回傳佈局字串

另外,除了回傳 Blade 視圖檔案之外,假如內容簡單的話,也可以在 render() 直接回傳一個包含 Blade 佈局的字串

class DeletePost extends Component
{
    public Post $post;

    public function delete()
    {
        $this->post->delete();
    }

    public function render()
    {
        return <<<'blade'
            <div>
                <button wire:click="delete">Delete Post</button>
            </div>
        blade;
    }
}

像這樣不需要搭配 Blade 視圖的組件,如果需要生成的話,可以加入 --inline選項,像底下這樣

php artisan make:livewire delete-post --inline


分享這篇文章:

關聯文章:

訂閱電子報,索取 Laravel 學習手冊

價值超過 3000 元,包含常用 Laravel 語法與指令!

Laravel 百萬年薪特訓營

從最基礎的 PHP 語法開始,包含所有你該知道的網頁基礎知識,連同 Laravel 從零開始一直到實戰,最後還將告訴你如何找好工作,讓你及早擁有百萬年薪