主题
懒加载
Livewire 允许你延迟加载组件,避免它们拖慢初始页面加载速度。
Lazy vs Defer
Livewire 提供了两种延迟组件加载的方式:
- 延迟加载 (
lazy): 组件在视口中可见时加载(当用户滚动到它们时) - 推迟加载 (
defer): 组件在初始页面加载完成后立即加载
这两种方法都能防止缓慢的组件阻塞初始页面渲染,但它们在组件实际加载的时机上有所不同。
基础示例
例如,假设你有一个 revenue 组件,它的 mount() 方法中包含一个缓慢的数据库查询:
php
<?php // resources/views/components/⚡revenue.blade.php
use Livewire\Component;
use App\Models\Transaction;
new class extends Component
{
public $amount;
public function mount()
{
// 缓慢的数据库查询...
$this->amount = Transaction::monthToDate()->sum('amount');
}
};
?>
<div>
Revenue this month: {{ $amount }}
</div>如果不使用延迟加载,这个组件会延迟整个页面的加载,让你的整个应用程序感觉很慢。
要启用延迟加载,你可以向组件传递 lazy 参数:
blade
<livewire:revenue lazy />现在,Livewire 不会立即加载该组件,而是跳过它,在没有它的情况下加载页面。然后,当组件在视口中可见时,Livewire 会发起网络请求来完全加载该组件。
Lazy 和 deferred 请求默认是隔离的
与 Livewire 中的其他网络请求不同,lazy 和 deferred 组件更新在发送到服务器时是相互隔离的。这通过并行加载每个组件来保持加载速度。阅读更多关于捆绑组件的内容 →
渲染占位符 HTML
默认情况下,Livewire 会在组件完全加载之前为其插入一个空的 <div></div>。由于组件最初对用户不可见,当组件突然出现在页面上时,可能会令人不快。
为了向用户显示组件正在加载,你可以定义一个 placeholder() 方法来渲染任何你喜欢的占位符 HTML,包括加载动画和骨架屏占位符:
php
<?php // resources/views/components/⚡revenue.blade.php
use Livewire\Component;
use App\Models\Transaction;
new class extends Component
{
public $amount;
public function mount()
{
// 缓慢的数据库查询...
$this->amount = Transaction::monthToDate()->sum('amount');
}
public function placeholder()
{
return <<<'HTML'
<div>
<!-- 加载动画... -->
<svg>...</svg>
</div>
HTML;
}
};
?>
<div>
Revenue this month: {{ $amount }}
</div>由于上述组件通过从 placeholder() 方法返回 HTML 来指定了“占位符”,用户将在组件完全加载之前在页面上看到一个 SVG 加载动画。
占位符和组件必须共享相同的元素类型
例如,如果你的占位符的根元素类型是 'div',你的组件也必须使用 'div' 元素。
通过视图渲染占位符
对于更复杂的加载器(如骨架屏),你可以从 placeholder() 返回一个 view,类似于 render()。
php
public function placeholder(array $params = [])
{
return view('livewire.placeholders.skeleton', $params);
}从延迟加载的组件传入的任何参数都将作为 $params 参数传递给 placeholder() 方法。
页面加载后立即加载
默认情况下,延迟加载的组件在进入浏览器视口之前不会完全加载,例如当用户滚动到它们时。
如果你希望在页面加载后立即加载组件,而不是等待它们进入视口,你可以使用 defer 参数:
blade
<livewire:revenue defer />现在这个组件将在页面就绪后立即加载,而不需要等待它在视口中可见。
你也可以使用 #[Defer] 属性来使组件默认为推迟加载:
php
<?php
namespace App\Livewire;
use Livewire\Component;
use Livewire\Attributes\Defer;
#[Defer]
class Revenue extends Component
{
// ...
}旧的 on-load 语法
你也可以使用 lazy="on-load",它的行为与 defer 相同。建议在新代码中使用 defer 参数。
传递属性
总的来说,你可以像处理普通组件一样处理 lazy 组件,因为你仍然可以从外部向它们传递数据。
例如,这是一个场景,你可能从父组件向 Revenue 组件传递时间间隔:
blade
<input type="date" wire:model="start">
<input type="date" wire:model="end">
<livewire:revenue lazy :$start :$end />你可以在 mount() 中接收这些数据,就像任何其他组件一样:
php
<?php // resources/views/components/⚡revenue.blade.php
use Livewire\Component;
use App\Models\Transaction;
new class extends Component
{
public $amount;
public function mount($start, $end)
{
// 昂贵的数据库查询...
$this->amount = Transactions::between($start, $end)->sum('amount');
}
public function placeholder()
{
return <<<'HTML'
<div>
<!-- 加载动画... -->
<svg>...</svg>
</div>
HTML;
}
};
?>
<div>
Revenue this month: {{ $amount }}
</div>然而,与普通组件加载不同,lazy 组件必须序列化或“脱水”任何传入的属性,并在组件完全加载之前在客户端临时存储它们。
例如,你可能想像这样向 revenue 组件传递一个 Eloquent 模型:
blade
<livewire:revenue lazy :$user />在普通组件中,实际的 PHP 内存中的 $user 模型将传递给 revenue 的 mount() 方法。然而,由于我们在下一次网络请求之前不会运行 mount(),Livewire 将在内部将 $user 序列化为 JSON,然后在处理下一个请求之前从数据库中重新查询它。
通常,这种序列化不应该导致应用程序中的任何行为差异。
强制默认为 lazy 或 defer
如果你想强制组件的所有使用都进行延迟加载或推迟加载,你可以在组件类上方添加 #[Lazy] 或 #[Defer] 属性:
php
<?php
namespace App\Livewire;
use Livewire\Component;
use Livewire\Attributes\Lazy;
#[Lazy]
class Revenue extends Component
{
// ...
}或者用于推迟加载:
php
<?php
namespace App\Livewire;
use Livewire\Component;
use Livewire\Attributes\Defer;
#[Defer]
class Revenue extends Component
{
// ...
}你可以在渲染组件时覆盖这些默认设置:
blade
{{-- 禁用延迟加载 --}}
<livewire:revenue :lazy="false" />
{{-- 禁用推迟加载 --}}
<livewire:revenue :defer="false" />捆绑多个 lazy 组件
默认情况下,如果页面上有多个延迟加载的组件,每个组件将并行发起独立的网络请求。这通常对性能是有益的,因为每个组件都是独立加载的。
然而,如果你在一个页面上有很多 lazy 组件,你可能希望将它们捆绑到一个网络请求中以减少服务器开销。
使用 bundle 参数
你可以使用 bundle: true 参数启用捆绑:
php
<?php
namespace App\Livewire;
use Livewire\Component;
use Livewire\Attributes\Lazy;
#[Lazy(bundle: true)]
class Revenue extends Component
{
// ...
}现在,如果同一页面上有十个 Revenue 组件,当页面加载时,所有十个更新将被捆绑并作为一个网络请求发送到服务器。
使用 bundle 修饰符
你也可以在渲染组件时使用 bundle 修饰符内联启用捆绑:
blade
<livewire:revenue lazy.bundle />这也适用于推迟组件:
blade
<livewire:revenue defer.bundle />或使用属性:
php
<?php
namespace App\Livewire;
use Livewire\Component;
use Livewire\Attributes\Defer;
#[Defer(bundle: true)]
class Revenue extends Component
{
// ...
}何时使用捆绑
使用捆绑的场景:
- 单个页面上有很多(5个以上) lazy 或 deferred 组件
- 组件的复杂度和加载时间相似
- 你想减少服务器开销和 HTTP 连接数
不使用捆绑的场景:
- 组件的加载时间差异很大(缓慢的组件会阻塞快速的组件)
- 你希望组件在各自就绪后立即显示
- 页面上只有几个 lazy 组件
旧的 isolate 语法
你也可以使用 isolate: false,它的行为与 bundle: true 相同。建议在新代码中使用 bundle 参数,因为它更明确地表达了意图。
全页面 lazy 加载
你可以使用路由方法对全页面 Livewire 组件进行 lazy 加载或 defer 加载。
Lazy 加载全页面
使用 ->lazy() 在组件进入视口时加载:
php
Route::livewire('/dashboard', 'pages::dashboard')->lazy();推迟全页面
使用 ->defer() 在页面加载后立即加载组件:
php
Route::livewire('/dashboard', 'pages::dashboard')->defer();禁用 lazy/defer 加载
如果组件默认为 lazy 或 deferred(通过 #[Lazy] 或 #[Defer] 属性),你可以使用 enabled: false 退出:
php
Route::livewire('/dashboard', 'pages::dashboard')->lazy(enabled: false);
Route::livewire('/dashboard', 'pages::dashboard')->defer(enabled: false);默认占位符视图
如果你想为所有组件设置默认的占位符视图,你可以在 /config/livewire.php 配置文件中引用该视图:
php
'component_placeholder' => 'livewire.placeholder',现在,当组件进行 lazy 加载且没有定义 placeholder() 时,Livewire 将使用配置的 Blade 视图(在此例中为 livewire.placeholder)。
在测试中禁用 lazy 加载
当对 lazy 组件进行单元测试,或测试包含嵌套 lazy 组件的页面时,你可能想禁用“lazy”行为,以便断言最终的渲染行为。否则,这些组件在测试期间将以占位符的形式渲染。
你可以轻松地使用 Livewire::withoutLazyLoading() 测试助手禁用 lazy 加载,像这样:
php
<?php
namespace Tests\Feature\Livewire;
use App\Livewire\Dashboard;
use Livewire\Livewire;
use Tests\TestCase;
class DashboardTest extends TestCase
{
public function test_renders_successfully()
{
Livewire::withoutLazyLoading() // [tl! highlight]
->test(Dashboard::class)
->assertSee(...);
}
}现在,当为此测试渲染 dashboard 组件时,它将跳过渲染 placeholder(),而是渲染完整的组件,就像根本没有应用 lazy 加载一样。