layui原生table不支持虚拟滚动,设置height仅触发css裁剪而非懒加载;需手动监听滚动、动态生成可视行并用事件委托绑定操作,同时缓存排序筛选状态并用requestidlecallback防卡顿。
layui 原生 table 不支持虚拟滚动,所谓“开启虚拟滚动模式”是误解——它根本没有这个开关,硬配 height 也不会自动启用懒加载或 dom 节流。
为什么设置 height 后表格还是卡死
很多人给 table.render() 加了 height: 500 就以为启用了虚拟滚动,结果万条数据一渲染,页面直接假死。这是因为 Layui 的 height 只触发 tbody 区域的 overflow-y: auto,但所有 <tr> 仍被一次性生成并挂载到 DOM 中。内存暴涨、重排阻塞、滚动掉帧,全是必然结果。
<ul>
<li>height 是纯 CSS 控制可视区裁剪,不是渲染策略切换开关</li>
<li>table.cache 会完整保存全部数据,即使只显示 20 行</li>
<li>没有 IntersectionObserver 或 requestIdleCallback 的介入点,无法按需创建/销毁 tr</li>
</ul>
<h3>真正能落地的轻量方案:手动接管 tbody + scroll 监听</h3>
<p>不引入 soulTable 或 vue-virtual-scroller 这类第三方,仅靠 Layui 表格结构 + 原生 API 就能跑通。关键动作只有三步:固定表头、动态生成可视行、防抖更新 tbody.innerHTML。</p>
<ul>
<li>表头用 <code>fixed: true(配合 height 才生效)或 thead { position: sticky; top: 0; }
elem.parentElement 的 scroll 事件,而非表格内部 —— 因为 Layui 不暴露容器 refscrollTop / rowHeight 算起始 index,再 allData.slice(start, start + visibleCount) 拼 HTML 字符串data-index),赋值给 tbody.innerHTML,避免 appendChild 频繁重排必须绕开的两个坑
虚拟滚动一上手就失效,往往栽在这两处:
-
table.on('tool()')或event绑定全失效:因为tbody.innerHTML = '...'会销毁所有已绑定事件节点,得改用事件委托,比如layui.$(tbody).on('click', '.operate-btn', handler) - 排序、筛选、分页状态丢失:每次重写 tbody 都是全新 DOM,
table.cache虽然还在,但table.config.where、table.config.sort不会自动带入新渲染,得在 scroll 触发前手动缓存,在 done 或重绘时传回
最易被忽略的是:没做 requestIdleCallback 或 setTimeout(..., 0) 包裹更新逻辑,导致连续滚动时频繁触发重绘,反而比原生还卡。真实场景下,宁可加 16ms 延迟,也不能同步算索引+拼字符串+写 innerHTML。











