嵌套
与许多其他基于组件的框架一样,Livewire 组件是可嵌套的,这意味着一个组件可以在自身内呈现多个组件。
但是,由于 Livewire 的嵌套系统与其他框架的构建方式不同,因此有一些重要的含义和约束需要了解。
在进一步了解 Livewire 的嵌套系统之前,充分了解 Livewire 如何水化组件非常有帮助。您可以通过阅读水化文档来了解更多信息。
每个组件都是一个孤岛
在 Livewire 中,页面上的每个组件都会跟踪其状态,并独立于其他组件进行更新。
例如,考虑以下Posts
和嵌套的ShowPost
组件
<?php namespace App\Livewire; use Illuminate\Support\Facades\Auth;use Livewire\Component; class Posts extends Component{ public $postLimit = 2; public function render() { return view('livewire.posts', [ 'posts' => Auth::user()->posts() ->limit($this->postLimit)->get(), ]); }}
<div> Post Limit: <input type="number" wire:model.live="postLimit"> @foreach ($posts as $post) <livewire:show-post :$post :key="$post->id"> @endforeach</div>
<?php namespace App\Livewire; use Illuminate\Support\Facades\Auth;use Livewire\Component;use App\Models\Post; class ShowPost extends Component{ public Post $post; public function render() { return view('livewire.show-post'); }}
<div> <h1>{{ $post->title }}</h1> <p>{{ $post->content }}</p> <button wire:click="$refresh">Refresh post</button></div>
以下是整个组件树的 HTML 在初始页面加载时可能的样子
<div wire:id="123" wire:snapshot="..."> Post Limit: <input type="number" wire:model.live="postLimit"> <div wire:id="456" wire:snapshot="..."> <h1>The first post</h1> <p>Post content</p> <button wire:click="$refresh">Refresh post</button> </div> <div wire:id="789" wire:snapshot="..."> <h1>The second post</h1> <p>Post content</p> <button wire:click="$refresh">Refresh post</button> </div></div>
请注意,父组件包含其呈现的模板和嵌套在其内部的所有组件的呈现的模板。
由于每个组件都是独立的,因此它们各自的 ID 和快照(wire:id
和wire:snapshot
)都嵌入在它们的 HTML 中,以便 Livewire 的 JavaScript 核心提取和跟踪。
让我们考虑一些不同的更新场景,以了解 Livewire 如何处理不同级别的嵌套。
更新子组件
如果您要单击子show-post
组件中的“刷新帖子”按钮,以下是将发送到服务器的内容
{ memo: { name: 'show-post', id: '456' }, state: { ... },}
以下是将发送回的 HTML
<div wire:id="456"> <h1>The first post</h1> <p>Post content</p> <button wire:click="$refresh">Refresh post</button></div>
这里需要注意的重要一点是,当在子组件上触发更新时,只有该组件的数据才会发送到服务器,并且只有该组件会被重新呈现。
现在让我们来看一下不太直观的场景:更新父组件。
更新父组件
作为提醒,以下是父组件 Posts
的 Blade 模板
<div> Post Limit: <input type="number" wire:model.live="postLimit"> @foreach ($posts as $post) <livewire:show-post :$post :key="$post->id"> @endforeach</div>
如果用户将“帖子限制”值从 2
更改为 1
,则仅在父组件上触发更新。
以下是请求有效负载示例
{ updates: { postLimit: 1 }, snapshot: { memo: { name: 'posts', id: '123' }, state: { postLimit: 2, ... }, },}
如你所见,仅父组件 Posts
的快照将发送到服务器。
你可能会问一个重要的问题:当父组件重新渲染并遇到子组件 show-post
时会发生什么?如果子组件的快照未包含在请求有效负载中,它将如何重新渲染子组件?
答案是:它们不会被重新渲染。
当 Livewire 渲染 Posts
组件时,它将为遇到的任何子组件渲染占位符。
以下是上述更新后 Posts
组件的渲染 HTML 示例
<div wire:id="123"> Post Limit: <input type="number" wire:model.live="postLimit"> <div wire:id="456"></div></div>
如你所见,仅渲染了一个子组件,因为 postLimit
已更新为 1
。但是,你还会注意到,除了完整的子组件之外,只有一个带有匹配 wire:id
属性的空 <div></div>
。
当在前端收到此 HTML 时,Livewire 会将此组件的旧 HTML 变形为此新 HTML,但会智能地跳过任何子组件占位符。
效果是,在变形后,父组件 Posts
的最终 DOM 内容将如下所示
<div wire:id="123"> Post Limit: <input type="number" wire:model.live="postLimit"> <div wire:id="456"> <h1>The first post</h1> <p>Post content</p> <button wire:click="$refresh">Refresh post</button> </div></div>
性能影响
Livewire 的“孤岛”架构对你的应用程序既有积极影响,也有消极影响。
此架构的一个优点是,它允许你隔离应用程序中昂贵的部分。例如,你可以将一个缓慢的数据库查询隔离到其自己的独立组件中,并且其性能开销不会影响页面的其余部分。
但是,此方法最大的缺点是,由于组件完全独立,组件间通信/依赖关系变得更加困难。
例如,如果你有一个从上述父组件 Posts
传递到嵌套组件 ShowPost
的属性,它将不会“响应”。因为每个组件都是一个孤岛,所以如果对父组件的请求更改了传递到 ShowPost
的属性的值,它不会在 ShowPost
中更新。
Livewire 克服了许多这些障碍,并为这些场景提供了专门的 API,例如:响应式属性、可建模组件以及$parent
对象。
掌握了嵌套 Livewire 组件如何操作的知识,你将能够在应用程序中何时以及如何嵌套组件方面做出更明智的决策。