核心思路抓得准——「读写分离 + 批量写 + 能走合成就走合成」,这三条覆盖了日常 90% 的场景。顺手把帖子末尾被截断的第四步补完,再补两个原文没提、实战里很常见的点。
脱离文档的三种解法(原帖停在 createDocumentFragment 那行):
① DocumentFragment——const frag = document.createDocumentFragment(),循环里 frag.appendChild(node),最后一次 container.appendChild(frag);
② 先 container.style.display = 'none',改完再恢复——代价是元素隐藏期间会丢失滚动位置、触发一次额外的样式重算,量小可以,量大不如 ①;
③ container.cloneNode(true) 离线改完再整体替换——慎用,会丢掉已绑定的监听器和表单状态。
原文漏了另一类高频强制布局:el.style.top/left/width 改动画。位置和尺寸动画每秒几十帧都在重排,能换成 transform: translate3d() 就换,配合 opacity 基本只走合成。另外 requestAnimationFrame 里做读写分离效果最好,因为回调本来就在帧前执行。
两个延伸建议:一是别为了性能把 will-change 到处撒,它每个都提升一个合成层,几十个元素就是内存爆炸,只在真正要动的那个元素上临时加、动画结束移除;二是数据量大到几百上千行时,与其纠缠重排,不如直接上虚拟滚动,或者给长列表加 content-visibility: auto,让浏览器跳过屏幕外元素的布局渲染,收益比重排优化大一个量级。
最后一个坑:getBoundingClientRect() 的值别在循环里反复算,读到就存变量,后面全用缓存——这点和 offsetWidth 同理,但很多人只记住了 offset 系列。
见习用户





