news 2026/9/10 21:13:08

ToolJet 表格组件服务端分页实战:用 limit/offset 分块加载 PostgreSQL 海量数据

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ToolJet 表格组件服务端分页实战:用 limit/offset 分块加载 PostgreSQL 海量数据

ToolJet 表格组件服务端分页实战:用 limit/offset 分块加载 PostgreSQL 海量数据

【免费下载链接】ToolJetOpen-source foundation of ToolJet AI - the enterprise app generation platform for internal tools, dashboards, business applications, workflows and AI agents. Build visually, from a prompt, or from Claude Code, Codex and Cursor over MCP 🚀项目地址: https://gitcode.com/GitHub_Trending/to/ToolJet

导读

当 Table 组件需要展示数十万乃至百万级数据时,把全部记录一次性加载到浏览器会让应用卡顿甚至崩溃。本指南将基于 ToolJet 的 Table 组件,演示如何借助数据库原生的limitoffset语法实现服务端分页(Server-side Pagination):每次只从数据库拉取当前页数据,并配合pageIndex暴露变量、Total records server sidePage changed事件构建完整的分页闭环。阅读完本文,你将掌握在 ToolJet 中配置分页查询、总数查询、分页按钮启用条件、加载态与翻页事件处理器的完整方法,并理解其底层实现原理。

适用场景与核心思路

服务端分页适用于 MySQL、PostgreSQL、MSSQL、MongoDB 等支持按块(chunk)取数的数据库。与客户端分页(一次性加载全部数据、在浏览器内翻页)不同,服务端分页的核心是:

  • 前端只持有当前页的数据;
  • 每次翻页都向数据库发起一次新的查询,通过limit控制每页行数、通过offset跳过前面页的数据;
  • 用一个独立的COUNT(*)查询告知 Table 组件总记录数,用于渲染总页数和"下一页/上一页"按钮。

在 ToolJet 中,这一切都由 Table 组件暴露的pageIndex变量驱动:它的默认值为1(见 table.js 中的exposedVariables定义),每当用户翻页,该值就会更新,从而触发查询重新执行。

第一步:编写分页查询,按块加载 PostgreSQL 数据

以 PostgreSQL 的users表为例,编写如下查询:

SELECT * FROM users ORDER BY id LIMIT 100 OFFSET {{(components.table1.pageIndex-1)*100}};

该查询每次只从users表拉取 100 行,实际返回的行数由 Table 组件中pageIndex(暴露变量)的当前值决定。例如:

  • 第 1 页:(1-1)*100 = 0,返回第 0~99 行;
  • 第 2 页:(2-1)*100 = 100,返回第 100~199 行;
  • 第 3 页:(3-1)*100 = 200,返回第 200~299 行。

对查询的逐段拆解如下:

子句作用
ORDER BY id对结果集按id列排序,保证翻页时数据顺序稳定、不重复不遗漏
LIMIT 100限制每次查询最多返回 100 行
OFFSET {{(components.table1.pageIndex-1)*100}}基于当前页码计算起始行号,实现翻页跳转

注意:LIMIT 100中的每页行数应与后面 Table 组件的"每页行数"设置保持一致,否则会出现页数与数据错位的问题。

第二步:编写总数查询

要让 Table 组件正确计算总页数,还需要一个返回总记录数的查询:

SELECT COUNT(*) FROM users;

该查询结果会被用作 Table 组件的Total records server side属性值。在 ToolJet 的查询结果中,COUNT(*)的返回值通常位于data[0].count,因此可直接以{{queries.<countquery>.data[0].count}}引用。

第三步:编辑 Table 组件属性

在 ToolJet 应用编辑器中,按以下步骤配置 Table 组件:

1. 绑定数据源

从组件库将 Table 组件拖入画布,在Data属性中填入分页查询的结果引用:

{{queries.<postgresquery>.data}}

其中<postgresquery>替换为你实际创建的 PostgreSQL 分页查询名称。这样表格每页展示的就是查询返回的当前页数据。

2. 启用服务端分页

开启Server-side pagination选项。在组件定义中,该开关对应serverSidePagination属性,是一个「Client side / Server side」二选一开关(见 table.js)。开启后,Table 组件会把分页交由数据库处理,而不是在浏览器端对已加载数据做切片。

3. 配置「上一页」按钮启用条件

点击Enable previous page button旁边的Fx,输入以下表达式。该条件会在当前页为第 1 页时禁用"上一页"按钮:

{{components.table1.pageIndex >=2 ? true : false}}

4. 配置「下一页」按钮启用条件

点击Enable next page button旁边的Fx,输入以下表达式。该条件会在当前页为最后一页时禁用"下一页"按钮:

{{components.table1.pageIndex < queries.<countquery>.data[0].count/100 ? true : false}}

这里的100必须与分页查询中的LIMIT 100一致;queries.<countquery>.data[0].count是总数查询返回的总记录数,两者相除即总页数。当前页索引小于总页数时,"下一页"按钮保持可用。

提示:在启用服务端分页后,分页页脚对上一页/下一页按钮的可用性判断正是读取enablePrevButton/enableNextButton这两个属性值(见 Pagination.jsx),所以上述两个Fx表达式的计算结果会直接控制按钮的禁用状态。

5. 设置服务端总记录数

Total records server side属性中填入总数查询结果:

{{queries.<countquery>.data[0].count}}

该值告诉 Table 组件数据集的总大小,组件据此计算出总页数并渲染分页按钮。从源码看,页脚在服务端分页模式下用Math.ceil(总记录数 / 每页行数)计算有效总页数(见 Pagination.jsx)。

下图展示了完成以上属性配置后的 Table 组件检查器面板:

6. 添加加载状态指示

为提升翻页体验,将 Table 组件的Loading state属性设置为分页查询的加载态,使查询执行期间表格显示加载指示器:

{{queries.<postgresquery>.isLoading}}

7. 绑定「Page changed」事件

选中 Table 组件的Page changed事件,点击New event handler新建事件处理器,选择Run Query动作,并在Query下拉框中选择第一步创建的分页查询(即从 PostgreSQL 表分块取数的那条查询)。

事件配置示例如下图:

配置完成后,每当用户翻页,分页查询就会重新执行,从 PostgreSQL 表中按块拉取对应页的数据:

底层实现:pageIndex 如何驱动整个分页闭环

理解源码有助于排查问题和按需扩展。ToolJet 新表格组件基于 TanStack React Table 构建,其关键链路如下:

1. 手动分页模式接管

在 useTable.js 中,manualPagination: serverSidePagination一行把分页切换为"手动模式"——开启服务端分页后,表格不再对已加载的data做客户端切片,只维护pagination状态(pageIndexpageSize),真正的数据切片由数据库完成。

2. 暴露变量与事件触发

在 TableExposedVariables.jsx 中,组件通过setExposedVariables({ pageIndex })把当前页码暴露出去——这正是分页 SQL 中{{(components.table1.pageIndex-1)*100}}能够动态求值的原因。同时,只有在用户通过页脚分页按钮/输入框翻页时才会触发onPageChanged事件(paginationBtnClicked标志位控制),从而避免程序化跳页造成重复请求。

3. 页脚渲染规则

在 Pagination.jsx 中可以看到服务端分页模式的完整渲染逻辑:

  • 若同时拿到serverSideRowsPerPagetotalRecords,则effectivePageCount = Math.ceil(totalRecords / serverSideRowsPerPage)
  • 若总记录数未知,则只显示当前页码,不显示跳页弹层(showFirstLastBtns为 false);
  • canGoToNextPage/canGoToPreviousPage在服务端模式下直接读取enableNextButton/enablePrevButton属性。

4. 每页行数的服务端取值

在 TableContainer.jsx 中,服务端分页开启时优先使用serverSideRowsPerPage(即属性面板里的 "Number of rows per page")作为pageSize,未设置时才回退到普通rowsPerPage

5. 属性默认值

initSlice.js 中初始化了这些属性:serverSidePagination默认falsetotalRecords默认10enablePrevButton/enableNextButton默认true,并且只要启用了serverSidePagination就会自动打开enablePagination

扩展到其他数据库

虽然本文示例使用 PostgreSQL,但分页公式本身是数据库无关的,可平移到其他支持LIMIT/OFFSET的数据库,例如 MySQL 与 MSSQL:

SELECT * FROM users ORDER BY id LIMIT 100 OFFSET {{(components.table1.pageIndex-1)*100}};

对于 MongoDB 这类文档型数据库,虽然关键字不同(使用skip代替offset),但"当前页决定跳过多少条记录"的算法完全一致,同样可以套用(pageIndex-1) * 每页行数的思路实现分块取数。

配置核对清单与常见问题

完成配置后,可对照以下清单做一次自查:

  • Data属性 ={{queries.<postgresquery>.data}}
  • Server-side pagination已开启
  • Enable previous page buttonFx ={{components.table1.pageIndex >=2 ? true : false}}
  • Enable next page buttonFx ={{components.table1.pageIndex < queries.<countquery>.data[0].count/100 ? true : false}}
  • Total records server side={{queries.<countquery>.data[0].count}}
  • Loading state={{queries.<postgresquery>.isLoading}}
  • Page changed事件绑定Run Query,指向分页查询

常见问题排查方向:

  • 总页数不准确:确认COUNT(*)查询的返回字段路径确实为data[0].count,且 "下一页按钮" Fx 中的除数与LIMIT的行数一致;
  • 翻页后数据重复或漏行:确认分页查询包含ORDER BY唯一性列(如id),否则数据库返回顺序不稳定会导致分页错位;
  • 按钮始终禁用:检查enableNextButton/enablePrevButton的 Fx 表达式返回类型是否为布尔值,以及总数查询是否成功执行;
  • 未触发翻页请求:确认事件处理器选择的是Page changed事件,且目标 Query 是分页查询而非总数查询。

按照以上步骤配置后,即使数据集达到数十万行,Table 组件也只需承担当前页数据的渲染开销,应用性能与交互流畅度都会得到显著提升。

相关参考

  • 组件属性与暴露变量定义:frontend/src/AppBuilder/WidgetManager/widgets/table.js
  • 分页状态与手动分页开关:frontend/src/AppBuilder/Widgets/NewTable/_hooks/useTable.js
  • 分页页脚渲染与总页数计算:frontend/src/AppBuilder/Widgets/NewTable/_components/Footer/_components/Pagination/Pagination.jsx
  • 暴露变量与事件触发逻辑:frontend/src/AppBuilder/Widgets/NewTable/_components/TableExposedVariables/TableExposedVariables.jsx
  • 表格属性初始化与默认值:frontend/src/AppBuilder/Widgets/NewTable/_stores/slices/initSlice.js
  • 服务端分页配置界面截图:docs/static/img/how-to/server-side/pagination-v2.png

【免费下载链接】ToolJetOpen-source foundation of ToolJet AI - the enterprise app generation platform for internal tools, dashboards, business applications, workflows and AI agents. Build visually, from a prompt, or from Claude Code, Codex and Cursor over MCP 🚀项目地址: https://gitcode.com/GitHub_Trending/to/ToolJet

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 21:11:58

情绪记录方法与自我觉察实践指南

1. 情绪记录与自我觉察的价值最近几天&#xff0c;我的情绪像坐过山车一样起伏不定。这种状态让我意识到&#xff0c;记录日常情绪波动其实是一件很有意义的事情。作为一个长期关注心理健康和个人成长的从业者&#xff0c;我想分享一些关于情绪记录的心得和方法。情绪日记不只是…

作者头像 李华
网站建设 2026/9/10 21:06:22

CANN/GE模型配置属性API

aclmdlConfigAttr 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对 PyTorch、TensorFl…

作者头像 李华