网页性能优化

网页的生成过程

  • HTML代码转化为DOM
  • CSS代码转化成CSSOM (CSS Object Model)
  • 结合DOMCSSOM,生成一颗渲染树 (包含每个节点的视觉信息)
  • 生成布局 (layout),即将所有渲染树的所有节点进行平面合成
  • 将布局绘制(paint)在屏幕上

最为耗时的在第四步和第五步
生成布局flow)和绘制paint),合称为渲染render

重排和重绘

网页生成的时候,至少会渲染一次。用户访问的过程中,还会不断重新渲染。

修改DOM、修改样式表、用户事件(鼠标悬停,页面滚动、输入框输入文字、改变窗口大小等)以上三种操作都会导致网页重新渲染。

重新渲染,就需要重新生成布局和重新绘制。前者叫做重排reflow),后者叫做重绘repaint)。重绘不一定需要重排,比如改变某个元素的颜色,就只会触发重绘, 因为布局没有变化,但是重排必然会导致重绘,比如改变某个元素的位置,就会同时触发重排和重绘,因为布局改变了。
提高网页性能,就是要降低重排和重绘的频率和成本,尽量少触发重新渲染

目前,浏览器已经很智能了,会尽量把所有变动集中在一起,排成一个队列,然后一次性执行,尽量避免多次重新渲染。
但是样式的写操作之后,有下面这些属性的读操作,会引发浏览器立即重新渲染

  • offsetTop/offsetLeft/offsetWidth/offsetHeight
  • scrollTop/scrollLeft/scrollWidth/scrollHeight
  • clientTop/clientLeft/clientWidth/clientHeight
  • getComputedStyle()

渲染的一般规则

  • 样式表越简单,重排和重绘就越快
  • 重排和重绘的DOM元素层级越高,成本就越高
  • table元素的重排和重绘,要高于div元素

提高性能的九个技巧

  • DOM的多个读操作,或多个写操作,应该放在一起。不要两个读操作之间,加入一个写操作。
  • 如果某个样式是通过重排得到的,那么最好缓存结果,避免下次用到的时候,浏览器又要重排。
  • 不要一条条地改变样式,而要通过class 或者csstext属性,一次性地改变样式
  • 尽量使用离线DOM,而不是真实的网面DOM,来改变元素样式。比如,操作Document Fragment对象,完成后再把这个对象加入DOM。再比如,使用 cloneNode() 方法,在克隆的节点上进行操作,然后再用克隆的节点替换原始节点。
  • 先将元素设为display: none(需要1次重排和重绘),然后对这个节点进行100次操作,最后再恢复显示(需要1次重排和重绘)。这样一来,你就用两次重新渲染,取代了可能高达100次的重新渲染。
  • position属性为absolute或fixed的元素,重排的开销会比较小,因为不用考虑它对其他元素的影响
  • 只在必要的时候,才将元素的display属性为可见,因为不可见的元素不影响重排和重绘。另外,visibility : hidden的元素只对重绘有影响,不影响重排。
  • 使用虚拟DOM的脚本库,比如React等。
  • 使用 window.requestAnimationFrame()window.requestIdleCallback() 这两个方法调节重新渲染

刷新率

很多时候,密集的重新渲染是无法避免的,比如scroll事件的回调函数和网页动画。网页动画的每一帧都是一次重新渲染,每秒低于24帧的动画,人眼就能感受到停顿,一般的网页动画,需要达到30帧和60帧,才能比较流程,如果达到每秒 70帧以上,就会极其流畅,60帧意味着 一秒之内进行60次重绘,每次重新渲染的时间不能超过16.66毫秒。

一秒之间能够完成多少次重新渲染,这个指标就被成为刷新率FPS,如果想达到60帧的刷新率,就意味着JavaScript线程每个任务的耗时,必须少于16毫秒。一个解决办法是使用Web Worker,主线程只用于UI渲染,然后跟UI渲染不相干的任务,都放在Worker线程。

Chrome浏览器开发者工具的Timeline面板

Timeline面板提供两种查看方式:横条的是”事件模式”(Event Mode),显示重新渲染的各种事件所耗费的时间;竖条的是”帧模式”(Frame Mode),显示每一帧的时间耗费在哪里。

网页性能优化方案

使用 window.requestAnimationFrame(),它可以将某些代码放到下一次重新渲染时执行,让读操作和写操作分离,把所有的写操作放到下一次重新渲染。

使用 window.requestIdleCallback(),它指定只有当一帧的末尾有空闲时间,才会执行回调函数。

window.requestIdleCallback(()=>{}) 只有当前帧的运行时间小于16.66ms时,函数才会执行。否则,就推迟到下一帧,如果下一帧也没有空闲时间,就推迟到下下一帧,以此类推。

window.requestIdleCallback(()=>{},5000) 它还可以接受第二个参数,表示指定的毫秒数。如果在指定 的这段时间之内,每一帧都没有空闲时间,那么函数将会强制执行。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享