返回

Laravel Livewire 函数式组件分页:解决 Property type not supported 错误

php

Laravel Livewire 函数式组件分页:解决 “Property type not supported” 错误

在使用 Laravel Livewire 构建应用时,如果数据量很大,一次性加载所有数据会导致性能问题。分页是解决这个问题的常用方法。官方文档主要展示了基于类的组件如何使用分页,而对于函数式组件,文档不够详细。本文将详细介绍如何在 Livewire 的函数式组件中使用分页,并解决常见的 "Property type not supported" 错误。

一、 问题

直接来看问题,官方的 bootcamp 示例代码如下:

<?php

use App\Models\Chirp;

use function Livewire\Volt\{state};

state(['chirps' => fn () => Chirp::with('user')->latest()->get()]);

?>

<div class="mt-6 bg-white shadow-sm rounded-lg divide-y">

@foreach ($chirps as $chirp)

    <div class="p-6 flex space-x-2" wire:key="{{ $chirp->id }}">
        {{-- ... --}}
    </div>

@endforeach

</div>

这段代码直接获取了所有的 Chirp 数据,没有分页。尝试按照文档修改,例如:

use Livewire\WithPagination;

$getChirps = fn () => $this->chirps = Chirp::paginate(10);

// 或

$getChirps = function () {
    return $this->chirps = Chirp::with('user')->paginate(15);
};

会遇到 "Property type not supported" 错误。这是因为 Livewire 函数式组件处理属性的方式与类组件不同。

二、 问题原因分析

错误信息 "Property type not supported" 表明 Livewire 无法处理你尝试传递给组件的数据类型。 在类组件中,WithPagination trait 会自动处理分页数据,但在函数式组件中,我们需要手动处理。 直接将 $this->chirps 设置为分页结果是不行的,因为函数式组件没有像类组件那样自动处理属性的机制。

三、 解决方案

下面给出几种在 Livewire 函数式组件中实现分页的方法。

3.1 方案一: 使用计算属性 (computed property)

Livewire 函数式组件支持计算属性。 我们可以创建一个计算属性来获取分页数据。

原理: 计算属性在每次组件渲染时都会重新计算,确保数据是最新的。 我们可以利用这个特性来获取分页数据。

代码示例:

<?php

use App\Models\Chirp;
use function Livewire\Volt\{computed, state};

$chirps = computed(fn() => Chirp::with('user')->latest()->paginate(10));

?>

<div class="mt-6 bg-white shadow-sm rounded-lg divide-y">

    @foreach ($chirps as $chirp)
        <div class="p-6 flex space-x-2" wire:key="{{ $chirp->id }}">
             {{-- ... Chirp 内容展示 --}}
        </div>
    @endforeach

    <div class="mt-4">
        {{ $chirps->links() }}
    </div>
</div>

解释:

  1. computed(fn() => ...):定义一个名为 $chirps 的计算属性。
  2. Chirp::with('user')->latest()->paginate(10):查询 Chirp 模型,关联 'user',按最新排序,每页 10 条数据。
  3. {{ $chirps->links() }}:在视图中显示分页链接。

3.2 方案二: 使用 state 和 自定义方法

我们可以用 state 定义一个初始状态,然后在自定义方法中更新这个状态。

原理: 通过自定义方法,我们能够更精细地控制数据更新,并手动处理分页。

代码示例:

<?php

use App\Models\Chirp;
use function Livewire\Volt\{state, on};

state(['page' => 1]);

$loadChirps = function () {
   $this->page = request()->query('page', 1); // 或者 $this->page = 1
    $perPage = 10;
    $chirps = Chirp::with('user')->latest()->paginate($perPage, ['*'], 'page', $this->page);
    return $chirps;

};

$chirps = $loadChirps(); // 调用函数来获取

?>

<div class="mt-6 bg-white shadow-sm rounded-lg divide-y">
    @foreach ($chirps as $chirp)
        <div class="p-6 flex space-x-2" wire:key="{{ $chirp->id }}">
             {{-- Chirp 内容 --}}
        </div>
    @endforeach
     <div class="mt-4">
        {{ $chirps->links() }}
    </div>
</div>

解释:

  1. state(['page' => 1]): 初始化一个 page 状态,默认值为 1.
  2. $loadChirps = function () { ... }: 定义一个函数来加载 Chirp 数据。
  3. $this->page = request()->query('page', 1); 获取当前页码, 或者使用 $this->page.
  4. Chirp::with('user')->latest()->paginate($perPage, ['*'], 'page', $this->page); 进行分页查询,使用 $this->page 作为当前页码。
  5. $chirps = $loadChirps(); 手动触发。
  6. {{ $chirps->links() }}:在视图中显示分页链接。

3.3 使用on更新属性(Event)

on函数提供了一种便捷方式来侦听事件并在事件触发时执行相应的操作。 利用这一特性, 在初始化完成进行赋值操作.

代码示例:

<?php

use App\Models\Chirp;
use function Livewire\Volt\{state, on, computed};

state(['chirps' => null]);

on([
    'mounted' => function () {
         $this->chirps = Chirp::with('user')->latest()->paginate(10);
    },
]);

?>

@if ($chirps)
<div class="mt-6 bg-white shadow-sm rounded-lg divide-y">

    @foreach ($chirps as $chirp)
        <div class="p-6 flex space-x-2" wire:key="{{ $chirp->id }}">
             Chirp content
        </div>
    @endforeach

    <div class="mt-4">
        {{ $chirps->links() }}
    </div>
</div>
@endif

解释:

  1. state(['chirps' => null]);: 使用空值初始化chirps
  2. 使用mounted 事件侦听器
  3. $this->chirps = Chirp::with('user')->latest()->paginate(10);: 利用paginate函数获取数据。
  4. 使用@if避免空值渲染。

3.4 方案选择建议

以上几种方案均能实现分页。

  • 方案一 最为简洁,推荐优先使用。
  • 方案二 在需要对分页逻辑进行更复杂控制时更为适用。
  • 方案三 提供初始化操作.

可以根据实际项目需要,选择最适合的方案。

四、 安全建议

无论使用哪种分页方法,都要注意以下安全事项:

  • 输入验证: 如果分页参数来自用户输入(例如 URL 中的 page 参数),务必进行验证,防止恶意输入导致的问题(例如传入非数字值)。

  • 防止过度分页: 限制每页显示的最大数据量,避免用户请求过大的页数导致服务器压力过大。

  • 权限控制: 如果Chirps数据存在权限区分, 需要检查用户是否拥有浏览特定数据的权限.

五、 进阶技巧

可以使用Livewire的wire:navigate来获得spa体验。
在使用方案一的时候, 如果需要在其他函数内获得当前的chirps, 可以再次使用计算属性,例如:

<?php
    use App\Models\Chirp;
    use Livewire\Attributes\On;
    use function Livewire\Volt\{computed};
    use Illuminate\Pagination\LengthAwarePaginator;

    $chirps = computed(fn () => Chirp::with('user')->latest()->paginate(10));

    $show_data = computed( fn() => $chirps->total() );

?>
    <span>
        总数据: {{$show_data}}
    </span>

如此,可以使用$show_data展示$chirps里面的信息。

有了这些技巧和注意事项,在 Livewire 函数式组件中实现分页就变得非常简单。