真正吃内存的是未做虚拟滚动的全量DOM渲染,Grid本身不导致内存过高;需将DOM节点压至20–50个,用占位元素维持滚动高度,并复用节点、禁用dense、避免闭包绑定与will-change滥用。

Grid 本身不导致内存过高,真正吃内存的是没做虚拟滚动的全量 DOM 渲染——1000 条数据就挂 1000 个 <div>,每个带样式、事件、布局计算,内存和重排开销直接翻倍。<h3>为什么 grid-auto-rows + 全量渲染会爆内存</h3>
<p>很多人以为设了 <code>grid-auto-rows: minmax(120px, auto) 就能“安全滚动”,但只要所有项都真实存在于 DOM 中,浏览器就得维护每项的 layout tree、paint layer、event listener,哪怕它们在视口外。滚动时触发的 layout、paint、composite 帧会频繁重算,尤其当子项含图片、SVG 或复杂 CSS(如 filter、transform)时,GPU 内存也跟着涨。
-
grid-auto-rows只影响行高推算逻辑,不减少 DOM 节点数 - 显式写
grid-template-rows: repeat(1000, 120px)更糟——它强制生成 1000 条轨道,哪怕只渲染前 20 项 - 用
auto-fit/auto-fill动态列数 + 长列表 = 每次 resize 都重算轨道,layout thrashing 高发
必须用虚拟滚动,而不是“优化 Grid”
Grid 是布局引擎,不是滚动方案。解决内存问题的唯一正路是把 DOM 节点数压到 20–50 个,并靠占位元素维持滚动高度。Grid 在这里只负责“怎么排这 20 个”,不是“怎么排全部 10000 个”。
- 用
getBoundingClientRect()或IntersectionObserver判断哪些项该挂载/卸载 - 父容器设固定
height或min-height,再用一个空<div> 占满剩余高度(<code>height: calc(1000 * 120px)),保证滚动条长度准确 - Grid 容器自身加
contain: strict,隔离渲染边界,防止虚拟滚动区域外的样式泄漏影响重绘范围 - 禁用
grid-auto-flow: dense——它会让浏览器反复回填空隙,增加 layout 计算负担 - 维护一个缓存池(比如 10 个已创建但未挂载的
<li>),卸载时removeChild后不销毁,而是pool.push(item) - 挂载前先
pool.pop(),没有再新建;避免 GC 峰值 - 子项内避免绑定闭包函数(如
onClick={() => doSomething(id)}),改用事件委托 +dataset传参,减少每个节点的内存 footprint - 慎用
will-change: transform——对虚拟滚动项加这个,反而让浏览器为每个项分配独立图层,内存不降反升
容易被忽略的 DOM 复用细节
光卸载 DOM 不够,高频滚动下反复 createElement + appendChild 也会卡顿。实际项目中得复用节点,否则 JS 执行时间会成为新瓶颈。
真正卡住的从来不是 Grid 的声明式语法,而是你没拦住那 998 个本不该出现在 DOM 里的节点。滚动容器的高度、占位逻辑、节点复用策略,这三个地方错一个,内存就稳稳往上走。











