table-layout: fixed 必须配合显式列宽(如<col width="120">)才生效;虚拟滚动是200行以上的硬性要求,需预设行高、缓冲区和phantomHeight;分页必须后端支持,前端禁用循环appendChild,改用DocumentFragment批量插入并requestAnimationFrame节流。

table-layout: fixed 必须配 才生效
只写 table-layout: fixed 没用,浏览器仍会遍历所有 <td> 内容算列宽。必须配合 <code><col> 显式声明宽度,比如:<col width="120"><col width="auto"><col width="80px">。其中 width="auto" 合法但仅限一列;多列 auto 会让 fixed 失效。别在 <td> 上写 <code>width 或 style.width,它们不参与列宽计算,纯属冗余。
超过 200 行就该上虚拟滚动
不是“建议”,是硬门槛。200 行以上,overflow-y: auto + 固定高度根本压不住 DOM 膨胀——节点还在,渲染管线照样满载。真正有效的只有虚拟滚动:只渲染视口内 ±1~2 屏的数据行,其余用占位 <tr> 填充高度。<p>关键约束有三个:</p>
<ul>
<li>必须预设每行高度(如 <code>rowHeight = 48),动态换行文本要强制统一或缓存实际高度
bufferCount ≥ Math.ceil(viewportHeight / rowHeight) + 2),否则快速滚动会白屏phantomHeight 必须严格等于 totalCount * rowHeight,否则滚动条比例失真分页必须前后端协同,不能只切数组
前端用 data.slice(page * size, (page + 1) * size) 对 5 万行以上就是自欺欺人:JSON 解析 + 数组切片本身已成瓶颈,且用户无法跳转末页。
立即学习“前端免费学习笔记(深入)”;
真实分页要后端支持:page=3&size=50,返回仅含这 50 条的 JSON,并附带 total 字段。前端拿到后立刻清空 tbody,用 DocumentFragment 或一次性 innerHTML 插入(注意 XSS,得 escapeHtml())。
首次加载别默认 size=100,size=20 更稳——大尺寸分页会让首屏 CLS(累计布局偏移)超标,用户看到内容前先被抖动劝退。
DOM 构建阶段最容易被忽略的卡点
问题不在 JS 执行慢,而在浏览器构建 DOM 树 + 样式计算 + layout 这一整套流程被压垮。几万个 <tr> 一次性塞进 <code>innerHTML,页面就假死。
实操底线:
- 禁用
for循环里反复appendChild,改用DocumentFragment批量插入 - 每批控制在 500–1000 行,插入后加
await new Promise(r => setTimeout(r, 0))让出主线程 - 滚动监听必须用
requestAnimationFrame节流,直接写onscroll在高刷屏上可能每帧触发多次 - 复用节点池比新建快得多,回收时必须重置
textContent、dataset和style.cssText,否则旧数据残留导致错行
最常被跳过的其实是语义结构:<thead>/<code><tbody> 分层缺失,表头无法合成层提升;没 <code>scope 或 headers,屏幕阅读器和 JS 操作都变慢——性能优化全白做。



















