资讯文章 资讯 · NEWS
一页塞 300 个条目,DOM 就爆了:目录站该在哪儿切分页
2026-10-22 约 1,552 字 预计阅读 4 分钟 yiyunkj
一
做目录站的人对"这一页放多少条"通常凭感觉:屏幕高一点就多放点,用户嫌翻页麻烦就少分页。但页面上的元素数量是有硬成本的,超过某个量级之后,不是加载慢一点的问题,是每次点击、每次展开都卡。

图片来源:网络,版权归原作者所有,侵权请联系删除
Lighthouse 的两条线
Chrome 的 Lighthouse 有一条诊断叫"避免 DOM 规模过大"。它的判定标准很明确:
- body 里的节点数超过约 800 个,给警告
- 超过约 1400 个,直接判不合格
另外两个参考值也常被一起看:最大嵌套深度超过 32 层,以及某个父元素下直接挂了 60 个以上子元素。前者典型症状是页面构建器一层套一层的 div,后者往往就是列表容器。
顺带说一句,这条诊断在 Lighthouse 13 之后合并进了 "Optimize DOM size" 这个洞察项里,位置变了,阈值没变。
数字是怎么涨上去的
算一笔账。一个普通的条目卡片,结构大概是:外层容器、图标容器、图标、标题容器、标题、简介、标签容器、两三个标签、收藏按钮、按钮里的 SVG。这一套下来 12 个节点不夸张。
| 每页条目数 | 列表区节点 | 加页头页脚后 | 判定 |
|---|---|---|---|
| 30 条 | 约 360 | 约 700 | 接近警告线 |
| 60 条 | 约 720 | 约 1060 | 警告区 |
| 100 条 | 约 1200 | 约 1540 | 不合格 |
| 300 条 | 约 3600 | 约 3950 | 远超 |
页头和页脚按 300 多个节点估的——带下拉的导航 200 个,页脚的站点地图链接 150 个,再加个客服挂件、Cookie 提示,随随便便就到这个数。
自己站上是多少,控制台一行就能看到:
document.querySelectorAll('*').length
为什么节点数会影响交互
浏览器的渲染管线里有几步是跟节点数量成正比的。样式计算要把每条 CSS 规则跟每个元素做匹配,一次重算就是节点数乘规则数;布局也一样,改一个元素的尺寸可能触发父级、兄弟、子级一串重排。
这也就是为什么节点多会直接拖累 INP。INP 量的是从用户操作到画面更新的延迟,一次点击如果触发了一棵四千节点树的样式重算,在中低端安卓机上几十到上百毫秒就这么没了,用户感觉到的就是"点了没反应"。
还有一类容易被忘掉的:display:none 的元素依然在 DOM 里,依然参与样式计算。移动端菜单在桌面端隐藏、五个标签页只显示一个其余四个用 CSS 藏起来,这些都是白付的成本。
切在多少合适
结合上面那张表,给一套可以直接用的参考值:
- 移动端:每页 30~40 条。屏幕小,本来就放不下太多,配合"加载更多"按钮
- PC 端:每页 60~80 条。三列或四列宫格,首屏两屏左右
- 搜索结果页:每页 20 条。用户在这个页面上是有明确目标的,翻页成本低,宁可多翻
如果非要一次展示很多,就用虚拟列表——只渲染视口内的几十个节点,滚动时复用。列表超过 100 项就该考虑这条路,react-window、vue-virtual-scroller 这类库都是现成的。
无限滚动和分页不是二选一
很多站改无限滚动是为了体验,结果把分页 URL 也一起删了。这就麻烦了:搜索引擎拿不到一个稳定的第 5 页地址,用户也没法把"第 5 页"发给别人。
正确做法是两者并存。分页链接 ?page=5 保留且可抓取,无限滚动只是给真人的增强:滚动加载时用 History API 把地址栏更新到对应的分页 URL。这样既满足了"不想点下一页"的用户,也没丢掉分页带来的可寻址性。
顺手能砍掉的三类节点
图标是最划算的一刀。一个内联 SVG 图标少说十几二十个节点,一页三十个图标就是几百个。改成 SVG sprite,用 <use> 引用,节点数直接掉一个数量级。
第二刀是砍无意义的包裹层。"为了 flex 加个 div""为了边框加个 div",这些都能用 CSS 直接作用在语义元素上解决。
第三刀是隐藏内容按需渲染。标签页只渲染当前那一panel,展开的详情在点击时才插入 DOM,别提前全塞进去再用 CSS 藏起来。