资讯动态

ThinkJS 2.x 分页组件 think-pagination 使用指南:从 countSelect 到模板渲染的完整实践

发布时间:2026/10/15 20:50:43 来源:尧图企业网站定制
后端Web框架【免费下载链接】thinkjsUse full ES2015 features to develop Node.js applications, Support TypeScript.项目地址https://gitcode.com/gh_mirrors/thi/thinkjs点击查看免费下载本指南以 packages/think-pagination/README_v2.md 为骨架系统讲解 ThinkJS 2.x 场景下 think-pagination 的安装、控制器调用、模板渲染方式并结合仓库源码深入剖析其 URL 自动生成、页码窗口算法与 HTML 输出原理。读完本文你将能够在 ThinkJS 2.x 项目中用一行函数生成完整的分页 HTML并理解分页数据pagerData在模型层的来源与字段结构。一、think-pagination 是什么think-pagination 是 ThinkJS 生态中的分页渲染组件它接收模型层countSelect返回的分页数据结合当前请求上下文http对象与自定义选项输出一段完整的ul分页条 HTML。该组件面向 ThinkJS 2.x 设计对应文档为 README_v2.md若你使用 ThinkJS 3.x则应参考 packages/think-pagination/README.md两代版本的 API 结构基本一致主要差异是第二参数由http对象演变为ctx对象。组件本身是一个纯函数模块主入口为 packages/think-pagination/index.js依赖think-helper提供的escapeHtml工具做参数转义见 packages/think-pagination/package.json。二、安装在项目根目录执行npm install think-pagination1.6.0安装完成后即可在控制器中通过import引入import pagination from think-pagination;三、控制器中使用一行生成分页 HTML官方文档给出的控制器示例完整如下对应 README_v2 的 controller 小节import pagination from think-pagination; export default class think.controller.base { async indexAction(){ let data await this.model(user).page(this.get(page)).countSelect(); let html pagination(data, this.http, {}); this.assign(pagination, html); } }关键链路可以拆解为三步this.get(page)从 GET 请求中读取当前页码this.model(user).page(page).countSelect()返回分页数据集datapagination(data, this.http, {})生成 HTML 字符串this.assign(pagination, html)将其注入模板变量。3.1 pagerData 从哪来page() 与 countSelect() 的源码印证pagerData并非普通查询结果而是模型层countSelect返回的专用分页结构。在 packages/think-model/src/model.js 中page方法负责设置分页的偏移量page(page, listRows this.config.pagesize) { if (helper.isArray(page)) { listRows page[1] || listRows; page page[0]; } page Math.max(parseInt(page) || 1, 1); listRows Math.max(parseInt(listRows) || 10, 1); this.options.limit [listRows * (page - 1), listRows]; return this; }几个值得注意的细节第二个参数listRows默认取this.config.pagesize即每页条数可在模型配置中控制支持page([2, 5])数组写法此时page为 2、listRows为 5页码与每页条数都经过Math.max(..., 1)兜底非法值如 0、负数、NaN会被修正为最小值 1 或 10。countSelect的实现位于 packages/think-model/src/model.js其返回结构是 think-pagination 的数据契约const result Object.assign({ count: count, totalPages: totalPage }, data); // data { pageSize: pagesize, currentPage: currentPage } result.data count ? await this.select(options) : []; return result;最终pagerData包含以下字段字段含义来源count满足条件的总记录数count()聚合查询totalPages总页数Math.ceil(count / pageSize)think-model/src/model.jscurrentPage当前页码parseInt(limit[0] / limit[1] 1)think-model/src/model.jspageSize每页条数即limit[1]think-model/src/model.jsdata当前页的数据列表有数据时执行select(options)无数据时为空数组think-pagination 的 HTML 生成正是消费totalPages、currentPage、count这三个字段因此传入的pagerData必须来自countSelect或与之结构一致的对象。四、模板中渲染两种模板引擎的差异由于pagination()返回的是 HTML 字符串不同模板引擎的注入方式不同4.1 ejs{%-pagination%}4.2 nunjucks{{pagination | safe}}注意 nunjucks 默认会对变量做 HTML 转义分页字符串中包含ul、a等标签必须通过safe过滤器原样输出否则会被转义为纯文本导致样式失效而 ejs 中{%- ... %}语法本身即按原始 HTML 输出。五、API 详解pagination(pagerData, http, options)函数签名与 README_v2 的 api 小节一致pagination(pagerData, http, options)参数说明pagerDatamodel.countSelect()返回的分页数据http当前请求的 http 对象ThinkJS 2.x用于自动生成分页 URLoptions分页渲染选项5.1 options 完整配置{ desc: false, //show description pageNum: 2, url: , //page url, when not set, it will auto generated class: , //pagenation extra class text: { next: Next, prev: Prev, total: count: ${count} , pages: ${pages} } }对照 packages/think-pagination/index.js 中的Object.assign默认值逐项说明如下配置项默认值作用descfalse是否在分页条首部显示总记录数与总页数描述pageNum2当前页前后各展示的页码个数页码窗口半宽url分页链接模板为空时基于当前查询参数自动生成class附加到ul classpagination ...上的额外 CSS 类名text.nextNext“下一页”链接文案text.prevPrev“上一页”链接文案text.totalcount: __COUNT__ , pages: __PAGE__描述模板__COUNT__与__PAGE__为占位符注意 README_v2 中text.total写为${count}/${pages}的模板字符串风格而实际源码index.js是用replace(__COUNT__, ...)、replace(__PAGE__, ...)替换占位符因此__COUNT__、__PAGE__才是真正生效的写法本指南以源码为准。自定义示例let html pagination(data, this.http, { desc: true, pageNum: 3, class: my-pagination, url: /user/list?page__PAGE__, text: { next: 下一页, prev: 上一页, total: 共 __COUNT__ 条共 __PAGE__ 页 } });5.2 desc 描述条的生成逻辑当desc: true时源码会先输出一个禁用态列表项index.jsconst total options.text.total .replace(__COUNT__, pagerData.count) .replace(__PAGE__, pagerData.totalPages); html li classdisabledspan${total}/span/li;即把pagerData.count总记录数与pagerData.totalPages总页数代入描述模板渲染为li classdisabledspan.../span/li。六、源码级原理剖析6.1 单页直接返回空函数入口处有一道守卫index.jsif (pagerData.totalPages 1) return ;总页数不大于 1 时无需分页直接返回空字符串避免无意义的分页条占用页面空间。仓库测试 packages/think-pagination/test/index.js 正是针对该行为it (totalPages 1, function() { var html pagenation({totalPages: 1}); assert.equal(html, ); });6.2 URL 自动生成保留当前查询参数未显式指定options.url时getPageUrl会基于httpctx上的查询参数自动拼装分页链接index.jsconst getPageUrl (options, ctx) { let pageUrl options.url; if (!pageUrl) { let prefix (options.prefix || ) ?; const querys []; for (const name in ctx.query) { if (name page) { continue; } querys.push(helper.escapeHtml(name) helper.escapeHtml(ctx.query[name])); } prefix querys.join(); if (querys.length) { prefix ; } pageUrl prefix page__PAGE__; } return pageUrl; };其行为要点自动跳过page参数避免生成page2page3这类重复参数保留其余全部查询参数如搜索关键字、排序条件保证翻页不丢失当前筛选状态参数名与参数值均经helper.escapeHtml转义从源头防范 XSS支持options.prefix为链接附加自定义前缀生成的模板形如?keywordxxxpage__PAGE____PAGE__是页码占位符。6.3 页码窗口算法pageNum 决定显示多少个页码getPageIndexindex.js计算当前页前后各pageNum个页码const getPageIndex (pagerData, options) { const num options.pageNum || 2; const page pagerData.currentPage | 0 || 1; const totalPages pagerData.totalPages; const pageIndex []; let start page - num; let stop page num; if (start 1) { start 1; stop start num * 2 1; } if (stop totalPages) { stop totalPages; start totalPages - num * 2 - 1; } for (let i start; i stop; i) { if (i 1 i totalPages) { pageIndex.push(i); } } return pageIndex; };以默认pageNum: 2为例当前页在第 3~倒数第 3 页之间时窗口为[currentPage-2, currentPage2]即最多显示 5 个页码靠近首页时窗口起点修正为 1并扩展窗口保证始终有num*21个页码靠近末页时窗口终点修正为totalPages并同步前移起点数组内每个元素都经过i 1 i totalPages过滤保证不越界。6.4 HTML 输出结构Bootstrap 风格分页条最终输出为 Bootstrap 风格的分页条index.js完整结构如下ul classpagination !-- desc: true 时 -- li classdisabledspan共 100 条共 10 页/span/li !-- 当前页 1 时 -- li classpreva href?page2Prev/a/li !-- 窗口前有剩余页时 -- lia href?page11/a/li li classdisabledspan.../span/li !-- 页码窗口 -- lia href?page44/a/li li classactivea href?page55/a/li lia href?page66/a/li !-- 窗口后有剩余页时 -- li classdisabledspan.../span/li lia href?page1010/a/li !-- 当前页 总页数时 -- li classnexta href?page6Next/a/li /ul各列表项语义列表项出现条件说明li.disabled spandesc: true总记录数 / 总页数描述li.prev acurrentPage 1上一页链接指向currentPage - 1li a首页窗口起始页 1指向第 1 页li.disabled span ...窗口起始页 2省略号占位li.active a当前页高亮当前页码li.disabled span ...窗口末页 总页数 - 1尾部省略号占位li a末页窗口末页 总页数指向最后一页li.next acurrentPage totalPages下一页链接指向currentPage 1class选项传入时ul变为ul classpagination ${options.class}便于叠加自定义样式如居中、尺寸、主题色。七、边界行为与最佳实践7.1 越界页码的兜底countSelect在pageFlag为布尔值时会对越界页码做兜底think-model/src/model.js当前页超过总页数时true修正为第 1 页、false修正为最后一页从而保证currentPage始终落在合法区间内分页条不会出现“当前页不存在”的异常状态。7.2 每页条数的默认值countSelect中options.limit options.limit || [0, this.config.pagesize || 10]think-model/src/model.js即每页默认 10 条若在模型配置中设置pagesize则分页、page()与countSelect会统一采用该值。7.3 模板安全输出nunjucks 中务必使用{{pagination | safe}}若希望限制分页条长度可调小pageNum如1表示当前页前后各 1 个页码若项目使用固定路由风格 URL推荐显式指定url模板如/user/list?page__PAGE__避免依赖自动生成。7.4 版本适用性说明ThinkJS 2.x阅读 README_v2.md安装think-pagination1.6.0第二参数传this.http控制器继承think.controller.baseThinkJS 3.x阅读 README.md第二参数传this.ctx其余 options 配置保持一致。八、总结think-pagination 以极小的 API 面一个函数、七个配置项完成了“数据驱动 自动 URL Bootstrap 风格 HTML”的完整分页闭环数据侧由model.page().countSelect()提供count、totalPages、currentPage等字段渲染侧由getPageUrl保留查询参数自动生成链接、getPageIndex控制页码窗口、入口守卫处理单页场景。结合 index.js 源码与 test/index.js 测试你可以放心地将它接入 ThinkJS 2.x 项目并在需要时通过options自由定制描述文案、页码窗口、URL 模板与附加样式。赞分享后端Web框架【免费下载链接】thinkjsUse full ES2015 features to develop Node.js applications, Support TypeScript.项目地址https://gitcode.com/gh_mirrors/thi/thinkjs点击查看免费下载相关推荐think-pagination 分页组件实战指南从 countSelect 到视图渲染的完整链路think pagination 分页组件实战指南从 countSelect 到视图渲染的完整链路 本篇技术指南以 ThinkJS 3 生态中的分页组件 th后端Web框架think-logger3ThinkJS 3.x 基于 log4js 的日志模块完整使用指南think logger3ThinkJS 3.x 基于 log4js 的日志模块完整使用指南 think logger3 是 ThinkJS 3.x 官方配套后端Web框架Yii 2 数据分页完整指南Pagination 对象与 LinkPager 组件的实战用法Yii 2 数据分页完整指南Pagination 对象与 LinkPager 组件的实战用法 当单页需要展示的数据量过大时常见的做法是将数据拆分到多个页面后端Web框架上一篇HS2-HF Patch实战指南从零开始打造你的专属游戏体验下一篇20K Vocab Builder 提示词拆解与复刻自适应词汇学习工作流的完整设计逻辑创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑