###### tags: `Laravel教學已拍` # [Laravel進階] Livewire核心觀念3:組件渲染 大家好,我是哥布林工程師,今天繼續來分享Livewire,來聊聊組件渲染相關的一些知識點 對了 如果你是第一次來到我們頻道的話,歡迎收看我們的課程 哥布林挨踢頻道是專注於電腦科學相關的課程 每週四下午6點都有新的影片上線 如果聽完今天的單元覺得對你有幫助的話 請幫忙給影片按著讚並訂閱我們的頻道唷 ## 加入組件到視圖 首先,如果要加入Livewire組件,最簡單的方式就是使用 `<livewire:組件名稱>` 標籤,像是這樣: ``` <div> <livewire:show-posts /> </div> ``` 或者也可以使用 Blade 的 @livewire 指令 `@livewire('show-posts’)` 事實上這兩個寫法的功能都是相同的,不過要採用標籤的形式來撰寫是限定 Laravel 7 以上的版本,這點請特別注意 假如你的 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() 來自己完成賦值,像是這樣 ``` class ShowPost extends Component { public $title; public $content; public function mount($post) { $this->title = $post->title; $this->content = $post->content; } ... } ``` 那什麼情況會導致賦值失敗勒? 根據我的測試如果你的參數名稱有下橫線的話就會無法正常傳值 所以命名時請特別注意 既然說到 mount() ,有件事你應該要知道 在 Livewire 組件中,你應該使用 mount() 而非建構子 __construct() 來完成屬性的初始化工作。而它就如同控制器的方法一樣,你能夠利用型別提示的依賴注入技巧在參數上 ``` 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> ``` 假如因為專案的需要,你想要改用其他視圖檔案而非預設的 layouts/app.blade.php,你能夠複寫 livewire.layout 設定檔的畫面這個選項來達到 `'layout' => ‘app.other_default_layout' ` 而如果你希望動態的調整而不透過設定,也就是在程式中去判斷要使用的視圖,你能夠在 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,假如你的父視圖像這樣: ``` <head> @livewireStyles </head> <body> @yield('content') @livewireScripts </body> ``` 你就需要把 layout() 改成使用 extends() ``` public function render() { return view('livewire.show-posts') ->extends('layouts.app'); } ``` 假如你需要指定組件所要渲染的 section 為何,你也能夠透過 section() 來加以指定 ``` public function render() { return view('livewire.show-posts') ->extends('layouts.app') ->section('content'); } ``` 假如你需要從組件類別將資料傳入 layout ,也沒問題。你能夠接著呼叫 layout(),並透過其第二參數來傳遞資料 ``` public function render() { return view('livewire.show-posts') ->layout('layouts.base', ['title' => 'Show Posts']) } ``` 看起來 Livewire 所提供的組件渲染功能是非常完整的,不管你的專案再複雜都能夠加以因應才對 ## 取得路徑參數 之前我們都是在控制器方法中去取用路徑參數,因為我們不再使用控制器, 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的這個值 ## 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() 內不需要做任何事情,讓工作變得更為簡單 但是特別提醒如果你環境的 PHP 版本低於7.4的話,將會出現語法錯誤的提示,表示你的 PHP 版本不足,請記得去做升級的動作 ``` //app\Http\Livewire\ShowPost.php class ShowPost extends Component { public Post $post; } ``` ## 組件的render() 最後聊一下 render() ,Livewire 組件的 render() 會在頁面初次載入的時候被呼叫。假如是很簡單的組件,你不需要自己定義 render(),因為生成的 Livewire 根組件就包含了一個動態的 render() 一般而言,在 render() 內被預期要回傳一個 Blade 視圖,因此,你能夠將之當做是一個控制器方法,請看下面這個例子: ``` //app/Http/Livewire/ShowPosts 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> ``` 至於組件視圖的部分,有一點是新朋友要特別注意的 就是根據官方文件的記載 請務必確認你的 Blade 視圖內只有一個根元素,以免出現錯誤 而根據我自己做測試如果根元素有兩個以上的話 組件內容是可以正常地顯示 但會否導致其他功能出現錯誤就不清楚了 建議還是最好避免,以免自爆 另外,除了回傳 Blade 視圖檔案之外 假如內容簡單的話,也可以在 render() 直接回傳一個包含 Blade 佈局的字串 也就是所謂的 inline 組件 關於 inline 組件更多的細節,可以參考我前一個關於建立組件的教學影片 有需要的可以參考上方資訊卡 inline 組件的類別長的就像是這樣 ``` //app/Http/Livewire/DeletePost 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; } } ``` 這裡直接提供懶人包,如果需要生成 inline 組件的話,可以加入 --inline 選項,像這樣 php artisan make:livewire delete-post --inline 以上就是今天關於組件渲染的所有知識點 如果你有任何問題歡迎在底下留言 假如你覺得這個單元有讓你學到東西的話 拜託給影片按個讚並訂閱我們的頻道 您的支持是讓頻道繼續下去最大的動力 我們下個單元再見,掰掰 ## 時間軸 影片開始 00:00 加入組件到視圖 00:50 傳入參數 05:00 從路由設定組件 12:43 取得路徑參數 24:17 Route Model Binding 28:02 組件的render() 31:02 ## 相關鏈接 【Livewire核心知識篇: 安裝與建立組件】 https://youtu.be/uHgFVTnSl5g 【Livewire快速入門】 https://youtu.be/H346Fat-0V0 【一頁式網站實作攻略】 https://youtu.be/wXg0ZdH9Kl0 【部落格網站實作攻略】 https://youtu.be/Jv7JPnWwxYg ## 行銷稿 各位朋友晚安,我是每天在空中陪著你的哥布林工程師,又有新的消息想要和大家報告 甚麼消息呢? 你猜猜今天是禮拜幾? 星期四,猜對了。也就是哥布林挨踢頻道發布新影片的日子,這周的影片就在剛剛上線拉,而這件事情讓我非常的興奮~ 影片的主題是談 Livewire 組件的渲染,目的在讓你明白該如何在視圖去使用組件。這主題很有用,但不是讓我興奮最主要的原因 我掛保證的告訴大家,這支影片是我從開始錄製影片以來最好的一支影片 從企劃 . 試拍 . 練習流程 . 正式拍攝 . 素材製作 . 口白優化 . 影片後製 這裡頭的每一個環節,都做了新的嘗試以及優化,目的是希望讓想要學習技術的朋友,能夠有更好的學習體驗 如果今天晚上沒有約會的話,何不花個35分鐘看下這支影片,累積自己的技術實力絕對是最好的投資,你說對嗎? 終於要把這份成果交給大家去評比,你覺得這支影片對你的學習有幫助嗎? 不管你是喜歡或者是覺得有那些地方可以改進,都歡迎留言告訴我唷! https://youtu.be/ifz8xUPYQTA