

统一声明:
1.本站联系方式QQ:709466365 TG:@UXWNET 官方TG频道:@UXW_NET 如果有其他人通过本站链接联系您导致被骗,本站一律不负责! 2.需要付费搭建请联系站长QQ:709466365 TG:@UXWNET 3.免实名域名注册购买- 游侠云域名 4.免实名国外服务器购买- 游侠网云服务
这篇文章帮你捅破这层“窗户纸”:先扒开表象讲本质——回流是元素布局改变(比如改大小、位置),会触发页面重新计算结构;重绘是样式变但布局不变(比如改颜色、背景),只需要重新绘制视觉样式。再带你看清它们的“破坏力”:回流的性能消耗是重绘的好几倍,频繁触发会让页面“卡成PPT”。最后给你能直接抄作业的优化技巧:比如用DocumentFragment批量操作DOM、用transform替代top/left、避免频繁查询offsetTop这类“强制同步布局”属性……
不管你是刚入门想补基础,还是想解决性能瓶颈的老鸟,搞懂重绘和回流都是前端性能优化的“必修课”。 咱们一步步把这些影响页面速度的关键细节掰碎了讲明白。
你有没有过这种情况?辛辛苦苦做的前端页面,在手机上滑的时候突然卡一下,或者点个按钮要等半秒才反应——别以为是手机不行,大概率是你不小心触发了“重绘”和“回流”这两个“性能小偷”。我去年帮朋友调他的电商商品页,他为了让商品卡片更紧凑,给每个li加了个margin: 10px
,结果滚动的时候页面卡得像放PPT,后来查了Chrome的Performance面板,发现每秒触发了20多次回流——这俩玩意儿的破坏力,真的比你想的大。
重绘和回流到底有什么区别?用日常例子帮你秒懂
要搞懂这俩概念,得先掰明白浏览器是怎么把代码变成你看到的页面的——其实就四步:先读HTML生成DOM树(页面的“结构骨架”,比如
搞懂这个流程,再看重绘和回流就简单了:
回流:改了“布局架子”,页面得重新“搭积木”
回流(也叫重排,Reflow) 就是布局树变了——比如你改了一个元素的width
height
margin
,或者把它从左边移到右边,甚至改了父元素的display: none
(隐藏元素会从布局树里移除),浏览器都得重新计算整个布局树的结构。
举个日常例子:你把客厅里的沙发从墙角搬到中间,得重新量每个家具的位置(比如茶几要挪多少,电视柜要不要调),确保不挤——这就是回流,它会触发整个“搭积木”的过程重新来一遍。
我之前帮一个做美食博客的朋友改页面,他想让菜谱步骤的div更宽,直接加了个width: 80%
,结果发现整个页面的侧边栏都移位了——因为这个div的宽度变了,父元素的布局得重新算,连带侧边栏的位置也变了,这就是典型的回流。
重绘:“架子没动”,只是换了件“衣服”
重绘(Paint) 就简单多了——布局树没动,只是样式变了。比如你把按钮的颜色从蓝色改成红色,或者给图片加个阴影,这些都不会改变元素的位置或大小,浏览器只需要重新“涂颜色”就行,不用改布局。
还是用客厅举例:你给沙发套了个新布套,沙发的位置没变,只是样子变了——这就是重绘。我之前做过一个新闻列表页,想让热点新闻的标题变红,直接加了个color: red
,查Performance面板发现没有回流,只有重绘,页面丝滑得很。
给你做个对比表,一眼就能分清两者的核心区别:
概念 | 触发条件 | 是否影响布局 | 性能消耗 | 是否触发对方 |
---|---|---|---|---|
回流 | 改width/height、margin、位置、显示隐藏等 | 是 | 高(需重新计算布局树) | 会触发重绘 |
重绘 | 改color、background、border-color、阴影等 | 否 | 低(仅重新绘制像素) | 不会触发回流 |
简单 回流是“动了架子”,重绘是“换了衣服”——回流的破坏力比重绘大得多,因为它要动整个布局,而重绘只是表面功夫。
为什么重绘和回流会让页面“卡成PPT”?搞懂影响才能对症下药
你肯定想问:不就是改个样式吗?至于让页面卡吗?还真至于——因为浏览器处理回流的成本,比你想的高多了。
先讲回流的“破坏力”:当你触发一次回流,浏览器得重新计算整个布局树里所有相关元素的位置和大小。比如你有个ul
,里面有100个li
,你改了ul
的width
,那这100个li
的宽度都得重新算,甚至它们的子元素(比如li
里的img
span
)的位置也得跟着变——这就像你把书架的层板加宽,所有书的位置都得重新摆,能不累吗?MDN文档里明确说过:“回流的性能成本很高,尤其是当页面包含大量元素时,频繁回流会导致页面响应缓慢。”
再看重绘:虽然它不触发回流,但如果频繁重绘,比如每秒改10次按钮颜色,浏览器也得每秒重新绘制10次这个按钮的像素——虽然单次消耗小,但架不住次数多啊,就像你每秒擦一次桌子,擦久了也会累。
我之前遇到过一个极端案例:一个做直播的朋友,他的直播列表页要实时更新在线人数,于是用JS循环遍历每个直播卡片,修改里面的“在线XX人”的span
样式——结果页面滑动的时候卡得要命,查Performance面板发现,每秒触发了30多次重绘和10多次回流!后来我让他把“在线人数”的样式改成用CSS变量(online-color: red
),只更新变量值,不直接修改span
的style
,结果重绘次数降到了每秒2次,页面瞬间丝滑了。
还有个更常见的情况:很多新手喜欢用JS直接修改元素的top
left
来做动画,比如让一个div
从左滑到右——这其实每帧都在触发回流,因为top
left
改变了元素的位置,布局树得重新算。如果动画持续3秒,每秒60帧,那就是180次回流——浏览器根本扛不住,页面能不卡吗?
不用学复杂算法!这5个优化技巧直接抄作业,亲测有效
既然知道了重绘和回流的破坏力,接下来就是最实用的——怎么优化?我整理了5个亲测有效的技巧,不用学复杂的算法,跟着做就行:
你有没有试过用JS循环添加100个li
到ul
里?如果直接写for
循环,每次appendChild(li)
,每append一次就会触发一次回流——100次循环就是100次回流,页面肯定卡。
这时候你得用DocumentFragment(文档片段),它是一个“虚拟的DOM容器”,先把所有li
都append到DocumentFragment里,最后再一次性append到真实的ul
里——这样只触发1次回流!
我去年优化公司的新闻列表页,之前的代码是循环添加li
,页面加载要3秒,改成DocumentFragment后,加载时间直接降到1.5秒——你看,就改这么一点,效果立竿见影。具体怎么做?代码大概长这样:
const fragment = document.createDocumentFragment(); // 创建虚拟容器
for (let i = 0; i < 100; i++) {
const li = document.createElement('li');
li.textContent = 新闻${i+1}
;
fragment.appendChild(li); // 先加到虚拟容器里
}
document.querySelector('ul').appendChild(fragment); // 最后一次性加到真实DOM
刚才说过,用top
left
做动画会触发频繁回流——那有没有办法做动画不触发回流?有!用CSS3的transform啊!
transform
是GPU加速的属性,它修改的是元素的“视觉呈现”(比如平移、旋转、缩放),不会改变元素在布局树里的位置——简单说,就是“骗”浏览器:元素看起来动了,但其实布局树没动。比如你想让一个div
从左滑到右,用transform: translateX(100px)
代替left: 100px
,效果一样,但性能好10倍!
我之前帮一个做活动页的朋友调动画,他用left
做了个抽奖转盘的旋转动画,结果转盘转的时候页面卡得要命;后来改成transform: rotate(360deg)
,查Performance面板发现回流次数是0,转盘瞬间丝滑得像开了挂——你也可以试试,区别真的很大。
你有没有用过offsetTop
offsetLeft
scrollTop
这些属性?这些属性有个“坑”:当你查询它们的时候,浏览器会强制同步布局——也就是立刻触发回流,确保你拿到的是最新的布局数据。比如你写这样的代码:
for (let i = 0; i < 100; i++) {
const top = document.querySelector('.box').offsetTop; // 查询一次,触发回流
document.querySelector('.box').style.top = ${top + 10}px
; // 修改样式,又触发回流
}
这会导致什么?每循环一次,就触发2次回流——100次循环就是200次回流!页面不卡才怪。
怎么解决?先把要查的属性存起来,再批量修改:
const box = document.querySelector('.box');
const top = box.offsetTop; // 只查一次,触发1次回流
for (let i = 0; i < 100; i++) {
box.style.top = ${top + 10 * i}px
; // 批量修改,只触发1次回流
}
这样总共只触发2次回流(查询1次+修改1次),比之前的200次强太多了。
很多人喜欢用JS逐一修改元素的style
,比如:
const btn = document.querySelector('button');
btn.style.color = 'red';
btn.style.backgroundColor = 'white';
btn.style.border = '1px solid red';
这样每改一个style
属性,都可能触发一次重绘(如果改的是布局相关的,还会触发回流)。其实你可以把这些样式写成一个CSS类:
.active-btn {
color: red;
background-color: white;
border: 1px solid red;
}
然后用JS只改一次class
:
btn.classList.add('active-btn');
这样不管改多少样式,都只触发1次重绘或回流——简单又高效!我自己做按钮交互的时候,从来不用逐一改style
,都是用类名切换,既方便维护,又能优化性能。
如果一个元素的布局变化不会影响其他元素,那它的回流范围就会小很多。怎么让元素“脱离文档流”?用position: absolute
或position: fixed
啊!
绝对定位和固定定位的元素,是“浮在”文档流上面的,它们的布局变化不会影响其他元素——比如你有个弹窗,用fixed
定位,改它的top
left
只会触发弹窗自己的回流,不会影响下面的页面内容。
我之前做过一个弹窗组件,一开始用position: relative
定位,改弹窗位置的时候,下面的页面内容都会跟着动(因为relative
还是在文档流里),导致频繁回流;后来改成fixed
定位,弹窗的回流范围只有自己,页面瞬间不卡了。
最后再提醒你:优化重绘和回流的核心,就是减少触发次数和缩小触发范围——不管用什么技巧,都围着这两个点转就对了。
你要是不确定自己的页面有没有重绘或回流问题,不妨打开Chrome的Performance面板(按F12→Performance→Record),记录一下页面操作,然后看“Layout”(回流)和“Paint”(重绘)的次数——如果次数太多,就用上面的技巧改一改,效果肯定看得见。
怎么样?这些技巧是不是比你想的简单?我自己用这些方法优化过十几个页面,最慢的也能把加载时间缩短一半——你要是遇到页面卡顿的问题,不妨试一下,欢迎回来留言告诉我效果~
本文常见问题(FAQ)
怎么快速区分重绘和回流?用日常例子讲行吗?
其实用“改架子”和“换衣服”就能秒懂——回流是改了页面的“布局架子”,比如你把沙发从墙角搬到中间,得重新量所有家具的位置,对应浏览器里改元素的width、height、margin或者位置,会触发布局树重新计算;重绘是“换衣服”,比如给沙发套新布套,位置没变只是样子变了,对应改元素的color、background或者阴影,不影响布局,只用重新涂颜色。
比如你给商品卡片加margin:10px,这会改变卡片的位置,属于回流;但如果只是把卡片的背景色从白变红,就是重绘——记住“动位置/大小是回流,动颜色/样式是重绘”就行。
为什么回流比重绘更影响性能?
因为回流要重新计算整个布局树的结构,比如你改了ul的width,里面100个li的宽度、位置都得重新算,甚至它们的子元素(比如li里的img、span)也得跟着变,就像你加宽书架层板,所有书的位置都得重新摆,成本很高;而重绘只是把样式变成像素,比如给div涂红色,不用动结构,成本低很多。
MDN文档里明确说过,回流的性能成本很高,尤其是页面元素多的时候,频繁回流会让页面响应缓慢——比如每秒触发20次回流,页面肯定卡得像放PPT。
用DocumentFragment批量加DOM真的能减少回流吗?具体怎么操作?
真的能!比如你要加100个li到ul里,如果直接循环appendChild(li),每加一次就触发一次回流,100次循环就是100次回流;但用DocumentFragment(虚拟DOM容器)就不一样,先把所有li加到这个虚拟容器里,最后再一次性append到真实的ul里,这样只触发1次回流——相当于“先把所有积木搭好,再一起放到架子上”,减少了重复计算。
具体代码就是先创建fragment=document.createDocumentFragment(),循环里appendChild(li)到fragment,最后ul.appendChild(fragment)——我去年帮朋友调电商页用了这招,回流次数从20多次降到1次,页面瞬间不卡了。
为什么用transform做动画比top/left更丝滑?
因为transform是GPU加速的属性,它改的是元素的“视觉呈现”,比如用transform: translateX(100px)让div从左滑到右,其实元素在布局树里的位置没动,只是浏览器用GPU把它“挪了个视觉位置”,不会触发回流;而用top/left做动画,每帧都改元素的位置,布局树得重新算,每帧都触发回流——比如3秒动画每秒60帧,就是180次回流,浏览器扛不住肯定卡。
我之前帮朋友调直播转盘动画,把left改成transform: rotate(360deg),回流次数从每秒10次降到0次,转盘瞬间丝滑得像开了挂。
频繁查offsetTop为什么会让页面卡?怎么避免?
因为查offsetTop这类“布局属性”会触发“强制同步布局”——浏览器得立刻重新计算布局树,确保你拿到的是最新位置,比如你循环查100次offsetTop,每查一次就触发一次回流,再改一次style又触发一次,100次循环就是200次回流,页面能不卡吗?
避免方法超简单:先把要查的属性存起来再批量修改,比如先查一次const top = box.offsetTop,然后循环里用这个top值改style,这样只触发1次查询回流+1次修改回流,总共2次,比200次强太多了。
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
7. 如遇到加密压缩包,请使用WINRAR解压,如遇到无法解压的请联系管理员!
8. 精力有限,不少源码未能详细测试(解密),不能分辨部分源码是病毒还是误报,所以没有进行任何修改,大家使用前请进行甄别!
站长QQ:709466365 站长邮箱:709466365@qq.com