layui.table卡顿主因是未启用真正的后端分页和虚拟滚动:需配置url支持page/limit参数、移除data项、设置height触发虚拟滚动、templet用字符串模板而非函数,treeTable分页会重载丢失展开态。

layui.table 用 page: true 却还是卡,是不是配置错了?
不是配置错,是没关掉默认的“性能炸弹”。page: true 只表示启用分页逻辑,但不等于自动走后端分页——它默认仍会尝试把全量数据拉下来再切片。真正起效的前提是:url 必须指向一个支持 page 和 limit 参数的接口,且后端要按这两个参数返回对应页的数据。
常见错误现象:Uncaught RangeError: Maximum call stack size exceeded、控制台反复打印“loading...”但没数据、滚动时表格直接白屏。
- 确认后端接口是否真的响应了
?page=1&limit=20这类参数,而不是忽略它们继续返回全部数据 - 去掉
data: []配置项,哪怕你本地有测试数组,也别和url同时存在,否则 layui 会优先用data并放弃请求接口 -
parseData回调里禁止对res.data做map、filter或深拷贝,这些操作在万级数据下会触发大量 GC
height 不设值,虚拟滚动就永远不会启动
layui.table 的虚拟滚动(virtual scroll)不是自动开启的,它只在明确设置了 height 且数据行数超过阈值时才生效。没设 height,哪怕你传了 10 万条数据,它也会试图一次性渲染所有 <tr>,主线程直接被锁死。
<p>实操建议:</p>
<ul>
<li>固定高度如 <code>height: 400,或自适应写法 height: 'full-150'(减去头部/工具栏高度)
minHeight 或 CSS height: 100% 替代,layui 内部只认 height 配置项height 后,检查 DOM 中是否出现 layui-table-box 和 layui-table-body 的滚动容器结构,没出现说明没生效templet 写成函数,性能直接断崖式下跌
列定义里用 templet: function(d) { return '<span>'+ d.name +'</span>'; } 看似灵活,但在大数据量下等于让 JS 引擎每行都执行一次函数调用+字符串拼接。1 万行 = 1 万次函数执行,比纯字符串模板慢 3–5 倍。
正确做法:
- 改用字符串模板:
templet: '{{d.name}}',layui 会用正则+replace批量处理,无函数调用开销 - 真需要逻辑判断,提取到
done回调里预处理字段,比如d.statusText = d.status === 1 ? '启用' : '禁用',然后模板里直接 {{d.statusText}} - 绝对不要在
templet里调用ajax、setTimeout或访问复杂嵌套对象(如d.user.profile.avatar),这些都会放大重绘压力
treeTable 分页切换后子节点丢失,根本不是 bug 是设计使然
treeTable 的分页是“整页重载”,不是局部刷新。每次切页,整个表格实例销毁重建,之前展开的节点状态(包括已加载的 children 数组)全部丢弃。这不是 bug,是它基于 table 模块的底层机制决定的。
能用的解法只有两个方向:
- 放弃分页,改用无限滚动(
flow组件 + 自定义容器),让树节点随滚动动态加载,避开页码切换 - 如果必须分页,就在
page的jump回调里手动保存当前页所有已展开节点的id到临时变量,切页后再用treeTable.expandNode(需自行封装)逐个展开——但这要求后端支持按节点 ID 批量查子节点 - 更现实的选择:换技术栈。实测
easyui treegrid在近 2000 条带操作列的数据下,加载时间稳定在 2s 内,且不丢展开态
最常被忽略的一点:treeTable 的性能瓶颈不在渲染,而在每次展开时对整个数据集做递归遍历匹配 parent_id。哪怕只展一层,它也会扫一遍全部数据找子项。这个逻辑没法绕开,只能接受或替换。


















