主题
深入理解嵌套
与许多其他基于组件的框架一样,Livewire 组件是可嵌套的——意味着一个组件可以在其内部渲染多个组件。
但是,由于 Livewire 的嵌套系统的构建方式与其他框架不同,因此有一些重要的影响和约束需要注意。
确保你首先了解水合
在了解有关 Livewire 嵌套系统的更多信息之前,完全了解 Livewire 如何水合组件是很有帮助的。你可以通过阅读水合文档来了解更多信息。
每个组件都是一个孤岛
在 Livewire 中,页面上的每个组件都独立于其他组件跟踪其状态并进行更新。
例如,考虑以下 Posts 和嵌套的 ShowPost 组件:
php
<?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(),
]);
}
}blade
<div>
Post Limit: <input type="number" wire:model.live="postLimit">
@foreach ($posts as $post)
<livewire:show-post :$post :key="$post->id">
@endforeach
</div>php
<?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');
}
}blade
<div>
<h1>{{ $post->title }}</h1>
<p>{{ $post->content }}</p>
<button wire:click="$refresh">Refresh post</button>
</div>以下是初始页面加载时整个组件树的 HTML 可能的样子:
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 组件中单击"Refresh post"按钮,以下是将发送到服务器的内容:
js
{
memo: { name: 'show-post', id: '456' },
state: { ... },
}以下是将发送回来的 HTML:
html
<div wire:id="456">
<h1>The first post</h1>
<p>Post content</p>
<button wire:click="$refresh">Refresh post</button>
</div>这里需要注意的重要一点是,当在子组件上触发更新时,只有该组件的数据被发送到服务器,并且只有该组件被重新渲染。
现在让我们看看不太直观的场景:更新父组件。
更新父组件
作为提醒,这是父 Posts 组件的 Blade 模板:
blade
<div>
Post Limit: <input type="number" wire:model.live="postLimit">
@foreach ($posts as $post)
<livewire:show-post :$post :key="$post->id">
@endforeach
</div>如果用户将"Post Limit"值从 2 更改为 1,则仅在父组件上触发更新。
以下是请求有效负载可能的样子的示例:
js
{
updates: { postLimit: 1 },
snapshot: {
memo: { name: 'posts', id: '123' },
state: { postLimit: 2, ... },
},
}如你所见,只有父 Posts 组件的快照被发送到服务器。
你可能会问自己一个重要的问题:当父组件重新渲染并遇到子 show-post 组件时会发生什么?如果它们的快照没有包含在请求有效负载中,它将如何重新渲染子组件?
答案是:它们不会被重新渲染。
当 Livewire 渲染 Posts 组件时,它将为遇到的任何子组件渲染占位符。
以下是在上述更新后 Posts 组件的渲染 HTML 可能的样子的示例:
html
<div wire:id="123">
Post Limit: <input type="number" wire:model.live="postLimit">
<div wire:id="456"></div>
</div>如你所见,只渲染了一个子组件,因为 postLimit 已更新为 1。但是,你还会注意到,完整的子组件被替换为只有一个空的 <div></div>,带有匹配的 wire:id 属性。
当前端接收到此 HTML 时,Livewire 将此组件的旧 HTML _变形_为此新 HTML,但会智能地跳过任何子组件占位符。
效果是,在_变形_之后,父 Posts 组件的最终 DOM 内容将是以下内容:
html
<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 组件如何运行的知识,你将能够就何时以及如何在应用程序中嵌套组件做出更明智的决定。