Skip to content
全部文档

分页

Laravel 的分页功能让你可以查询数据的子集,并为用户提供在这些结果的之间导航的能力。

由于 Laravel 的分页器是为静态应用设计的,在非 Livewire 应用中,每次翻页都会触发一次完整的浏览器访问,前往包含目标页码的新 URL(?page=2)。

然而,在 Livewire 组件内使用分页时,用户可以在同一页面上翻页。Livewire 会在幕后处理一切,包括用当前页码更新 URL 查询字符串。

基本用法

下面是在 show-posts 组件中使用分页的最基本示例,每次只显示十条文章:

WARNING

必须使用 WithPagination trait

要使用 Livewire 的分页功能,包含分页的每个组件都必须使用 Livewire\WithPagination trait。

php
<?php // resources/views/components/⚡show-posts.blade.php

use Livewire\Attributes\Computed;
use Livewire\WithPagination;
use Livewire\Component;
use App\Models\Post;

new class extends Component {
    use WithPagination;

    #[Computed]
    public function posts()
    {
        return Post::paginate(10);
    }
};
blade
<div>
    <div>
        @foreach ($this->posts as $post)
            <!-- ... -->
        @endforeach
    </div>

    {{ $this->posts->links() }}
</div>

可以看到,除了通过 Post::paginate() 方法限制显示的文章数量外,我们还会用 $this->posts->links() 渲染翻页导航链接。

关于使用 Laravel 分页的更多信息,请参阅 Laravel 完整的分页文档

禁用 URL 查询字符串跟踪

默认情况下,Livewire 的分页器会在浏览器 URL 的查询字符串中跟踪当前页,例如:?page=2

若仍想使用 Livewire 的分页工具,但不想跟踪查询字符串,可以使用 WithoutUrlPagination trait:

php
use Livewire\WithoutUrlPagination;
use Livewire\WithPagination;
use Livewire\Component;

class ShowPosts extends Component
{
    use WithPagination, WithoutUrlPagination; // [tl! highlight]

    // ...
}

现在,分页仍会按预期工作,但当前页不会出现在查询字符串中。这也意味着当前页不会在页面切换之间被保留。

自定义滚动行为

默认情况下,Livewire 的分页器在每次翻页后会滚动到页面顶部。

你可以通过向 links() 方法的 scrollTo 参数传入 false 来禁用此行为,例如:

blade
{{ $posts->links(data: ['scrollTo' => false]) }}

或者,你也可以向 scrollTo 参数提供任意 CSS 选择器,Livewire 会在每次导航后找到匹配该选择器的最近元素并滚动到该处:

blade
{{ $posts->links(data: ['scrollTo' => '#paginated-posts']) }}

重置页码

在对结果排序或筛选时,通常希望将页码重置回 1

因此,Livewire 提供了 $this->resetPage() 方法,让你可以在组件中的任意位置重置页码。

下面的组件演示了在搜索表单提交后用该方法重置页码:

php
<?php // resources/views/components/⚡search-posts.blade.php

use Livewire\Attributes\Computed;
use Livewire\WithPagination;
use Livewire\Component;
use App\Models\Post;

new class extends Component {
    use WithPagination;

    public $query = '';

    public function search()
    {
        $this->resetPage();
    }

    #[Computed]
    public function posts()
    {
        return Post::where('title', 'like', '%'.$this->query.'%')->paginate(10);
    }
};
blade
<div>
    <form wire:submit="search">
        <input type="text" wire:model="query">

        <button type="submit">Search posts</button>
    </form>

    <div>
        @foreach ($this->posts as $post)
            <!-- ... -->
        @endforeach
    </div>

    {{ $this->posts->links() }}
</div>

现在,如果用户原本在结果的第 5 页,然后按下「Search posts」进一步筛选结果,页码会重置回 1

可用的翻页方法

除了 $this->resetPage(),Livewire 还提供了其他有用的方法,可在组件中以编程方式翻页:

MethodDescription
$this->setPage($page)将分页器设为指定页码
$this->resetPage()将页码重置为 1
$this->nextPage()前往下一页
$this->previousPage()前往上一页

多个分页器

由于 Laravel 和 Livewire 都使用 URL 查询字符串参数来存储和跟踪当前页码,若同一页面包含多个分页器,为它们指定不同名称很重要。

为了更清楚地说明问题,请看下面的 show-clients 组件:

php
<?php // resources/views/components/⚡show-clients.blade.php

use Livewire\Attributes\Computed;
use Livewire\WithPagination;
use Livewire\Component;
use App\Models\Client;

new class extends Component {
    use WithPagination;

    #[Computed]
    public function clients()
    {
        return Client::paginate(10);
    }
};

可以看到,上面的组件包含一组分页后的客户。若用户导航到该结果集的第 2 页,URL 可能如下:

text
http://application.test/?page=2

假设页面还包含同样使用分页的 show-invoices 组件。为了独立跟踪每个分页器的当前页,需要为第二个分页器指定名称,例如:

php
<?php // resources/views/components/⚡show-invoices.blade.php

use Livewire\Attributes\Computed;
use Livewire\WithPagination;
use Livewire\Component;
use App\Models\Invoice;

new class extends Component {
    use WithPagination;

    #[Computed]
    public function invoices()
    {
        return Invoice::paginate(10, pageName: 'invoices-page');
    }
};

现在,由于已向 paginate 方法添加了 pageName 参数,当用户访问发票的第 2 页时,URL 会包含如下内容:

text
https://application.test/customers?page=2&invoices-page=2

在命名分页器上使用 Livewire 的翻页方法时,必须将页名称作为额外参数提供:

php
$this->setPage(2, pageName: 'invoices-page');

$this->resetPage(pageName: 'invoices-page');

$this->nextPage(pageName: 'invoices-page');

$this->previousPage(pageName: 'invoices-page');

挂钩页面更新

Livewire 允许你通过在组件中定义以下任一方法,在页面更新之前和之后执行代码:

php
<?php // resources/views/components/⚡show-posts.blade.php

use Livewire\Attributes\Computed;
use Livewire\WithPagination;
use Livewire\Component;
use App\Models\Post;

new class extends Component {
    use WithPagination;

    public function updatingPage($page)
    {
        // Runs before the page is updated for this component...
    }

    public function updatedPage($page)
    {
        // Runs after the page is updated for this component...
    }

    #[Computed]
    public function posts()
    {
        return Post::paginate(10);
    }
};

命名分页器钩子

前面的钩子只适用于默认分页器。若使用命名分页器,必须用分页器的名称定义方法。

例如,下面是名为 invoices-page 的分页器钩子的样子:

php
public function updatingInvoicesPage($page)
{
    //
}

通用分页器钩子

若不想在钩子方法名中引用分页器名称,可以使用更通用的替代方法,只需将 $pageName 作为钩子方法的第二个参数接收:

php
public function updatingPaginators($page, $pageName)
{
    // Runs before the page is updated for this component...
}

public function updatedPaginators($page, $pageName)
{
    // Runs after the page is updated for this component...
}

使用简易主题

你可以使用 Laravel 的 simplePaginate() 方法代替 paginate(),以获得更快的速度和更简单的实现。

用此方法分页结果时,只会向用户显示下一页上一页导航链接,而不是每个页码的单独链接:

php
public function render()
{
    return view('show-posts', [
        'posts' => Post::simplePaginate(10),
    ]);
}

关于简易分页的更多信息,请参阅 Laravel 的「simplePaginator」文档

使用游标分页

Livewire 也支持使用 Laravel 的游标分页——一种在大数据集中更快的分页方法:

php
public function render()
{
    return view('show-posts', [
        'posts' => Post::cursorPaginate(10),
    ]);
}

使用 cursorPaginate() 代替 paginate()simplePaginate() 时,应用 URL 的查询字符串会存储一个编码后的游标,而不是标准页码。例如:

text
https://example.com/posts?cursor=eyJpZCI6MTUsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0

关于游标分页的更多信息,请参阅 Laravel 的游标分页文档

使用 Bootstrap 代替 Tailwind

若你的应用使用 Bootstrap 而不是 Tailwind 作为 CSS 框架,可以将 Livewire 配置为使用 Bootstrap 风格的分页视图,而不是默认的 Tailwind 视图。

为此,请在应用的 config/livewire.php 文件中设置 pagination_theme 配置值:

php
'pagination_theme' => 'bootstrap',

INFO

发布 Livewire 的配置文件

在自定义分页主题之前,必须先运行以下命令,将 Livewire 的配置文件发布到应用的 /config 目录:

```shell php artisan livewire:config ```

修改默认分页视图

若要修改 Livewire 的分页视图以适配应用风格,可以用以下命令发布它们:

shell
php artisan livewire:publish --pagination

运行该命令后,以下四个文件会插入到 resources/views/vendor/livewire 目录:

View file nameDescription
tailwind.blade.php标准 Tailwind 分页主题
tailwind-simple.blade.php简易 Tailwind 分页主题
bootstrap.blade.php标准 Bootstrap 分页主题
bootstrap-simple.blade.php简易 Bootstrap 分页主题

文件发布后,你就完全掌控它们了。在模板中用分页结果的 ->links() 方法渲染分页链接时,Livewire 会使用这些文件,而不是自带的视图。

使用自定义分页视图

若希望完全绕过 Livewire 的分页视图,可以用以下两种方式之一渲染自己的视图:

  1. Blade 视图中的 ->links() 方法
  2. 组件中的 paginationView()paginationSimpleView() 方法

第一种做法是直接把自定义分页 Blade 视图名称传给 ->links() 方法:

blade
{{ $posts->links('custom-pagination-links') }}

渲染分页链接时,Livewire 现在会查找 resources/views/custom-pagination-links.blade.php 视图。

通过 paginationView()paginationSimpleView()

第二种做法是在组件中声明 paginationViewpaginationSimpleView 方法,返回你希望使用的视图名称:

php
public function paginationView()
{
    return 'custom-pagination-links-view';
}

public function paginationSimpleView()
{
    return 'custom-simple-pagination-links-view';
}

分页视图示例

下面是一个未加样式的简易 Livewire 分页视图示例,供你参考。

可以看到,你可以在模板中直接使用 Livewire 的翻页辅助方法(如 $this->nextPage()),只需在按钮上添加 wire:click="nextPage"

blade
<div>
    @if ($paginator->hasPages())
        <nav role="navigation" aria-label="Pagination Navigation">
            <span>
                @if ($paginator->onFirstPage())
                    <span>Previous</span>
                @else
                    <button wire:click="previousPage" wire:loading.attr="disabled" rel="prev">Previous</button>
                @endif
            </span>

            <span>
                @if ($paginator->onLastPage())
                    <span>Next</span>
                @else
                    <button wire:click="nextPage" wire:loading.attr="disabled" rel="next">Next</button>
                @endif
            </span>
        </nav>
    @endif
</div>

对于仅视觉层面的加载状态(如透明度变化),也可以改用 Livewire 自动的 data-loading 属性配合 Tailwind 类:

blade
<button wire:click="nextPage" class="data-loading:opacity-50" rel="next">
    Next
</button>

了解更多关于加载状态 →

另见