Vue3实现Pagination分页组件(三)自定义控件布局

1,801 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

写在前面

在 B 端的 web 开发中,分页组件或者叫分页器,是较为常用的控件之一,通常配合表格或列表,实现数据量大的情况下,分页拆解数据的功能。

由于完整实现的篇幅较大,所以我分成了以下几部分逐个讲解,这里是第三讲:自定义控件布局,如果你需要查看完整的源代码实现,请访问Pagination 分页组件

  • 基础实现
  • 分页操作控件
  • 自定义控件布局
  • 限制最大页码数

需求拆分

由于分页组件包含有不少的小控件,包括prev 上一页、next-下一页、pager-页码、jumper-输入跳转、sizes-每页条数选择器、total-总条数;通常情况下,用户希望可以自主的选择需要展示的控件,以及它们的顺序,甚至可以自定义显示的文案。那么根据这些要求,则可以列出以下难点:

  • 如何定义用户传入的自定义配置?
  • 如何实现按需加载对应控件和顺序?
  • 组件的属性和事件触发如何处理?

定义用户传入配置

对于大多数流行组件库的分页组件而言,通用的做法是将组控件名以逗号链接的方式,作为属性传入,接收后解析需要加载的控件和顺序,这里使用的就是这个方案,例如:

layout: { type: String, default: "total,prev,pager,next", validator: layoutValidator }, // 自定义组件布局

/**
* 自定义布局配置验证
* @param {String} v
* @returns Boolean
*/
const layoutValidator = (v) => v.split(",").length !== 0;

加载控件并渲染

先说下逻辑方面,我们获取了用户需要加载的控件之后,需要准备一个componentsList用于生成最后的列表以供<component> 组件遍历渲染,如下:

<template v-for="(item, index) in componentsList" :key="`p-${index}`">
    <component :is="item.component" v-bind="item.bind" @[item.event]="item.eventName"></component>
</template>

根据上面我们可以确定,渲染需要包含组件以及bindeventeventName,用于绑定属性和动态处理事件名和回调方法。

这里以「上一页」Prev控件为例,定义该控件需要绑定的属性和事件

const prevComponents = computed(() => {
    return {
        bind: {
            text: props.prevText,
            disabled: props.disabled || currentPage.value === 1,
        },
        event: "click",
        eventName: setCurrentPage,
    };
});

// 以下省略
const nextComponents = ...
const totalComponents = ...
const sizesComponents = ...
const jumperComponents = ...
const pagerComponents = ...

引入控件,并创建映射保存起来

import total from "./components/total.vue";
import prev from "./components/prev.vue";
import pager from "./components/pager.vue";
import next from "./components/next.vue";
import jumper from "./components/jumper.vue";
import sizes from "./components/sizes.vue";

const layout_map = { total, totalPages, prev, pager, next, jumper, sizes, simple };

准备好数据后,便可以进行componentsList的组装:

// 返回一个包含组件模板、属性以及事件的组件列表
const componentsList = computed(() => {
    const { layout, simple } = props
    const layout_list = simple ? ["prev", "simple", "next"] : layout.split(","); // 可自定义的分页布局配置

    const components = [];

    // 各组件的属性以及事件配置
    const component_map = {
        total: totalComponents.value,
        totalPages: totalPagesComponents.value,
        prev: prevComponents.value,
        pager: pagerComponents.value,
        next: nextComponents.value,
        jumper: jumperComponents.value,
        sizes: sizesComponents.value,
        simple: simpleComponents.value,
    };

    layout_list.map((name) => {
        components.push({ name, component: layout_map[name], ...component_map[name] });
    });

    return components;
});

欢迎阅读其它文章