JavaScript 如何减少重绘回流

打开手机里的电商APP,你滑动页面时图片、文字能立刻跟着动;刷短视频时,新内容加载出来的速度也快得没察觉——这些流畅体验的背后,离不开浏览器的渲染机制。但要是操作不当,页面就会变卡、甚至卡顿,这大多和“重绘”“回流”这两个环节有关。很多前端新手会忽略这部分细节,今天就用生活化的例子,说说怎么用JavaScript给浏览器“减负”,减少重绘回流。

先搞懂:重绘和回流到底是什么?

举个最简单的例子:你贴在墙上的海报(网页元素),如果只是换了颜色、改了字体,海报本身的位置和大小没动,这就叫“重绘”——相当于你给海报重新刷了一遍颜料,没碰它的尺寸和位置。但如果把海报挪到墙的另一边,或者把它撕下来换了个更大的,整个墙面的布局就变了,这就叫“回流”——相当于重新调整了墙面的空间,连带着周围的海报、挂画可能都要跟着挪位置,工作量比重绘大得多,也更费时间。

JavaScript 如何减少重绘回流

而JavaScript的操作,很多时候会不小心触发“连锁回流”:比如你连续改5个元素的位置,浏览器会每次都调整布局,相当于跑了5遍“重新排布局”的流程,页面能不卡吗?

别让JavaScript“反复折腾”——合并DOM操作

日常开发里,最容易触发回流的就是频繁修改DOM(网页里的各个元素)。就像你整理书桌,一会儿挪笔,一会儿移笔记本,一会儿摆杯子,每动一样都重新摆一遍整个桌面,效率肯定低。正确的做法是:把要改的内容一次性弄好,再放到页面里。

举个例子:要是你要给网页加10个新的商品卡片,别循环着每次创建一个卡片就插进去,那样会触发10次回流。不如先在内存里把10个卡片都做好,最后一次性把整个集合插进去,这样只触发1次回流。用代码说就是,别这么写:

`javascript

for (let i = 0; i < 10; i++) {

const card = document.createElement('div');

card.className = 'card';

container.appendChild(card);

}

`

而要这么写:

`javascript

const fragment = document.createDocumentFragment();

for (let i = 0; i < 10; i++) {

const card = document.createElement('div');

card.className = 'card';

fragment.appendChild(card);

}

container.appendChild(fragment);

`

这里的DocumentFragment就像“临时收纳盒”,所有DOM操作都在盒子里完成,最后一次放到页面,大幅减少回流次数。

用“离线修改”躲过重绘回流的触发时机

有时候你需要改多个样式,比如要把一个按钮变宽、变色、移位置,要是直接一个个改,会触发多次重绘回流。就像你要给照片修三个地方,别每次改一处就打印一次,不如先在PS里修完所有地方,再导出成品。

JavaScript里也有这样的“PS工具”——document.hidden之外,还有display: none。把元素设为display: none,它就从页面布局里“消失”了,这时候改它的样式,不会触发任何回流;等所有修改都完成,再把它设为display: block,浏览器只需要重新排一次这个元素的布局就行,效率高很多。

比如你要做一个弹窗,加载动画时要改多个元素的大小、颜色,不如先把弹窗设为隐藏,修改完再显示,就不会在修改过程中让页面反复重绘。

把频繁的样式计算“攒起来”再处理

还有个容易踩的坑:连续读取元素的布局属性,比如offsetWidth、clientHeight,然后马上修改样式,这会让浏览器“被迫”立刻计算布局,相当于每读一次就重新排一次,白忙活。

举个例子:你要把两个盒子摆成并排,算好第一个盒子的宽度后,马上改第二个盒子的宽度,这时候浏览器会先算第一个的宽度,再改第二个,又要重新算布局。正确的做法是,把所有要读的属性先读完,再统一修改。

就像你要给两个人分桌子,先量好两个人的身高,再一起调整椅子高度,而不是量完一个就调一个,避免反复折腾。

其实这些方法本质上都是一个道理:减少浏览器的重复劳动,让它一次做完该做的事。前端优化没有什么复杂的黑科技,只要记得别让它“反复干活”,就能让页面更流畅。

上一篇 Swift语言的核心特性与应用场景,一文带你了解iOS开发的热门编程语言