奥门威斯Web页面的性能优化

Web性能优化系列(2):剖析页面绘制时间

2015/04/15 · CSS,
HTML5,
JavaScript ·
性能优化

本文由 伯乐在线 –
刘健超-J.c
翻译,sunbiaobiao
校稿。未经许可,禁止转载!
英文出处:www.deanhume.com。欢迎加入翻译组。

最近,我参加了在伦敦举办的Facebook移动开发者大会。在那天期间,有很多的交谈,但真正让我关注的是一场关于性能的,名为“让m.facebook.com更快”的交流会,它的主题是关于Facebook如何不断努力改善网页性能和从中汲取的经验。

Facebook开发团队是使用Chrome
Cannry
来测试网页CSS性能的。Google Chrome
Canary
拥有Chrome的最新特性,并允许试用一些即将成为Chrome标准版本的,可行的最新特性。考虑到Chrome
Canary作为一个为开发者和尝鲜者专门设计的“预览版”,所以有时候会因Chrome开发团队的快速迭代而导致一些B
UG。尽管如此,它仍然有一些很棒的开发者工具帮助你测试网页性能

奥门威斯 1

在这篇文章里,我展示如何使用Chrome
Canary的开发者工具去定位你的CSS中的一部分,这部分CSS可能会导致页面滚动缓慢和影响页面的绘制时间。当浏览器加载和绘制页面时,为了“绘制”并让内容显示在屏幕上,需要遍历所有可见元素。由于这依赖于布局和复杂的CSS,你可能会发现绘制时间会很长。这会导致网页看起来忽动忽停和响应较慢。这种缓慢滚动也称为jank(jank是Android系统的一个专业术语,指的是屏幕上流畅动态画面中断的卡顿现象)。在移动设备上滚动页面时,浏览器会使劲地绘制复杂的CSS,这时这种情况更加明显。

即使页面的加载时间十分快,也仍然值得去研究页面的绘制时间。不同设备对CSS属性有着不一样的反应,但无论如何,能提高性能总是一件很好的事。为了进行测试,首先得去Google
Chrome
网站下载Chrome
Canary。一旦安装完成,就可以打开你想测试的网页。HTML5
Rocks
网站里有一个很好的案例网站,我们使用它来证明高耗能CSS属性的操作,会增加页面的绘制时间。

奥门威斯 2

一旦你打开到这个网页,按下F12,会弹出Chrome的开发者工具。然后在开发者工具的底部右侧点击设置按钮,开启测试页面渲染性能的设置。

奥门威斯 3

点击后会显示一个允许你更改设置的控制板。

奥门威斯 4

因为我们要测试页面的渲染性能,所以选择“Enable continuous page
repainting
(页面持续重新绘制)“和 “Show FPS
meter(显示FPS仪表)”**。如果你关闭设置面板,查看你的网页,你应该会看到下面的图片在页面右上角。

奥门威斯 5

该表显示以毫秒为单位的当前页面绘制所需时间,而右侧显示了当前图表的最小与最大值。另外,也显示了最近80帧的树状图。这个图表的强大之处是它不断试图重新绘制页面,使得页面好像是第一次加载。这允许你精确定位因CSS影响的绘制问题,而不用每次重新加载页面。无论你的改变是否产生影响,树状图都会持续监测。

如果我们详细查看这个页面的HTML和CSS,你会看到其中一个div添加了一些CSS效果。

奥门威斯 6

这个div有border-radius(圆角)和投影属性。当移除box-shadow属性,再观察FPS
meter在绘制时间的变化。

奥门威斯 7

哇!正如你从图表可看出,页面绘制时间有一个令人关注的变化。通过简单地将border-radius属性移除,就可以证明这个改变能让页面的绘制时间显著减少。当你更新或改变CSS属性时,这个图表就立即下降。在同一个元素上同时使用box-shadowborder-radius,会导致非常重的绘制负担,这是因为浏览器不能为之做出优化。如果有一个元素需要频繁的重复绘制,你应该在建立网页时时刻记住这点。

这是一个很好的,在Google IO
网站上的视频,它更深入地阐述绘制时间,并介绍一些减少网页“jank(卡顿)”的技巧。

想更进一步学习绘制时间的优化,看看这些链接。

祝测试愉快!

打赏支持我翻译更多好文章,谢谢!

打赏译者

Web页面的性能

本文由 伯乐在线 – 鸭梨山大 翻译,sunbiaobiao 校稿。未经许可,禁止转载!
英文出处:gokulkrishh.github.io。欢迎加入翻译小组奥门威斯,。

Web性能优化系列(1):Web性能优化分析

2015/04/08 · CSS,
HTML5,
JavaScript ·
性能优化

本文由 伯乐在线 –
鸭梨山大
翻译,sunbiaobiao
校稿。未经许可,禁止转载!
英文出处:gokulkrishh.github.io。欢迎加入翻译组。

如果你的网站在1000ms内加载完成,那么会有平均一个用户停留下来。2014年,平均网页的大小是1.9MB。看下图了解更多统计信息。

奥门威斯 8

网站的核心内容需要在1000ms内呈现出来。如果失败了,用户将永远不会再访问你的网站。通过降低页面加载的时间,很多著名公司的收入和下载量有显著的提升。比如

  • Walmart 每降低100ms的加载时间,
    他们的收入就提高1%.
  • Yahoo
    每降低400ms的加载时间,他们的访问量就提升9%。
  • Mozilla
    将他们的页面速度提升了2.2秒,每年多获得了1.6亿firefox的下载量。

打赏支持我翻译更多好文章,谢谢!

任选一种支付方式

奥门威斯 9
奥门威斯 10

赞 2 收藏
评论

我们每天都会浏览很多的Web页面,使用很多基于Web的应用。这些站点看起来既不一样,用途也都各有不同,有在线视频,Social
Media,新闻,邮件客户端,在线存储,甚至图形编辑,地理信息系统等等。虽然有着各种各样的不同,但是相同的是,他们背后的工作原理都是一样的:

如果你的网站在1000ms内加载完成,那么会有平均一个用户停留下来。2014年,平均网页的大小是1.9MB。看下图了解更多统计信息。

网站优化的步骤

  1. 设定性能预算。
  2. 测试当前的性能。
  3. 找出导致性能问题的地方。
  4. 最后,duang,使用优化特技。

下面有几种方法可以提升你的页面性能,让我们来看看

关于作者:刘健超-J.c

奥门威斯 11

前端,在路上…
个人主页 ·
我的文章 ·
19 ·
    

奥门威斯 12

用户输入网址

奥门威斯 8

速度指标

速度指标是指页面的可视部分被呈现在浏览器中的平均速度。表示为毫秒的形式,并且取决于viewport的大小。请看下图(用视频帧的形式展现页面加载时间,以秒为单位)。

速度指标越低越好。

奥门威斯 14

速度指标可以通过Webpagetest
来测试(由Google维护)

浏览器加载HTML/CSS/JS,图片资源等

网站的核心内容需要在1000ms内呈现出来。如果失败了,用户将永远不会再访问你的网站。通过降低页面加载的时间,很多著名公司的收入和下载量有显著的提升。比如

长话短说

Webpage test 有很多特性,比如在不同的地方用不同的浏览器跑多个测试。
还可以测算其他的数据比如加载时间,dom元素的数量,首字节时间等等…

例如:查看amazon在webpagetest上的测试结果

可以看看这个视频,了解由
Patrick Meenan
讲解的关于webpagetest的更多信息(需要梯子)。

浏览器将结果绘制成图形

  • Walmart 每降低100ms的加载时间,
    他们的收入就提高1%.
  • Yahoo 每降低400ms的加载时间,他们的访问量就提升9%。
  • Mozilla 将他们的页面速度提升了2.2秒,每年多获得了1.6亿firefox的下载量。

渲染阻塞

如果你知道浏览器如何运行,那么你应该知道HTML, CSS,
JS是怎么被浏览器解析的以及其中哪个阻塞了页面的渲染。如果你不知道,请看下图。

奥门威斯 15

点击how a browser
works了解更多浏览器工作原理(作者为Tali
Garsiel 和Paul
Irish).

用户通过鼠标,键盘等与页面交互

网站优化的步骤

  1. 设定性能预算。
  2. 测试当前的性能。
  3. 找出导致性能问题的地方。
  4. 最后,duang,使用优化特技。

下面有几种方法可以提升你的页面性能,让我们来看看

浏览器渲染的步骤

  1. 首先浏览器解析HTML标记去构造DOM树(DOM = Document Object Model
    文档对象模型)
  2. 然后解析CSS去构造CSSOM树( CSSOM = CSS Object Model CSS对象模型)
  3. 在将DOM和CSSOM树结合成渲染树之前,JS文件被解析和执行。

现在你知道浏览器如何进行解析了,让我们看看是哪一部分阻塞了渲染树的生成。

奥门威斯 16

速度指标

速度指标是指页面的可视部分被呈现在浏览器中的平均速度。表示为毫秒的形式,并且取决于viewport的大小。请看下图(用视频帧的形式展现页面加载时间,以秒为单位)。

速度指标越低越好。

奥门威斯 14

速度指标可以通过Webpagetest 来测试(由Google维护)

1. 阻塞渲染的CSS

有人认为CSS阻塞了渲染。在构造CSSOM时,所有的CSS都会被下载,无论它们是否在当前页面中被使用。

为了解决这个渲染阻塞,跟着下面的两个步骤做

  1. 将关键CSS内嵌入页面中,即将最重要的(首次加载时可见的部分页面所使用到的)style写入head中的
    <style></style>里。
  2. 移除没用到的CSS。

那么我是如何找出没用到的CSS的呢。

  1. 使用Pagespeed
    Insight
    去得到像未使用的CSS,阻塞渲染的CSS和JS文件等等的统计数据。例如:Flipkart的Pagespeed
    Insight统计结果。
  2. 使用Gulp任务,如gulp-uncss或是使用Grunt
    任务,如grunt-uncss。如果你不知道他们是什么,请阅读我之前的文章。

这些种类繁多的页面,在用户体验方面也有很大差异:有的响应很快,用户很容易就可以完成自己想要做的事情;有的则慢慢吞吞,让焦躁的用户在受挫之后拂袖而去。毫无疑问,性能是影响用户体验的一个非常重要的因素,而影响性能的因素非常非常多,从用户输入网址,到用户最终看到结果,需要有很多的参与方共同努力。这些参与方中任何一个环节的性能都会影响到用户体验。

长话短说

Webpage test 有很多特性,比如在不同的地方用不同的浏览器跑多个测试。
还可以测算其他的数据比如加载时间,dom元素的数量,首字节时间等等…

例如:查看amazon在webpagetest上的测试结果 。

可以看看这个视频,了解由 Patrick
Meenan 
讲解的关于webpagetest的更多信息(需要FQ)。

##专业小贴士

  1. 使用CSS
    Stats保证页面中完全没有未被用到的元素,唯一的样式和字体等等。
  2. Pagespeed Insight Chrome
    插件.
  3. Tag Counter Chrome
    插件.

宽带网速

渲染阻塞

如果你知道浏览器如何运行,那么你应该知道HTML, CSS,
JS是怎么被浏览器解析的以及其中哪个阻塞了页面的渲染。如果你不知道,请看下图。

奥门威斯 15

点击how a browser
works了解更多浏览器工作原理(作者为Tali
Garsiel 和Paul
Irish).

2. 渲染阻塞的JavaScript

如果在解析HTML标记时,浏览器遇到了JavaScript,解析会停止。只有在该脚本执行完毕后,HTML渲染才会继续进行。所以这阻塞了页面的渲染。

为了解决它

在<script></script>标签中使用 async或defer特性。

  1. <script
    async>将会在HTML解析时下载该文件并在下载完成后马上执行。
  2. <script defer>
    将会在HTML解析式下载该文件并在HTML解析完成后执行。

例如: async and defer都在Google Analytics中使用

点击查看async
和defer的浏览器支持。

DNS服务器的响应速度

浏览器渲染的步骤

  1. 首先浏览器解析HTML标记去构造DOM树(DOM = Document Object Model
    文档对象模型)
  2. 然后解析CSS去构造CSSOM树( CSSOM = CSS Object Model CSS对象模型)
  3. 在将DOM和CSSOM树结合成渲染树之前,JS文件被解析和执行。

现在你知道浏览器如何进行解析了,让我们看看是哪一部分阻塞了渲染树的生成。

内存泄漏

内存泄漏和页面臃肿
是前端开发者所要面对的问题之一。让我们来看看如何发现并解决内存泄漏。

在JavaScript中寻找内存泄漏

使用Chrome Task
Manager(任务管理器)去检测app所使用的内存以及js内存(总体内存+实时内存)。如果你的内存一直随着你的每次操作而提高,那么你可以怀疑有内存泄漏了。

下面是Chrome Task Manager的截图。

奥门威斯 19

服务器的处理能力

1. 阻塞渲染的CSS

有人认为CSS阻塞了渲染。在构造CSSOM时,所有的CSS都会被下载,无论它们是否在当前页面中被使用。

为了解决这个渲染阻塞,跟着下面的两个步骤做

  1. 将关键CSS内嵌入页面中,即将最重要的(首次加载时可见的部分页面所使用到的)style写入head中的 <style></style>里。
  2. 移除没用到的CSS。

那么我是如何找出没用到的CSS的呢。

  1. 使用Pagespeed
    Insight 去得到像未使用的CSS,阻塞渲染的CSS和JS文件等等的统计数据。例如:Flipkart的Pagespeed
    Insight统计结果。
  2. 使用Gulp任务,如gulp-uncss或是使用Grunt
    任务,如grunt-uncss。如果你不知道他们是什么,请阅读我之前的文章。

Chrome DevTools分析

使用 Heap Profiler 去查看内存泄漏。打开Chrome devTools 然后点击profiles
标签,接着选中 take heap snapshot。如果你不了解Chrome
DevTools,请阅读之前的文章.

奥门威斯 20

Heap Profiler有四个快照视图(snapshot view)

  1. Summary 视图 –
    展示对象的总体数量以及它们的实例总数,浅部(Shallow)大小(对象本身的内存大小)以及保留(Retained)大小(自动GC发生后所释放的内存大小+无法执行到的对象的内存大小)。
  2. Comparison 视图-
    用于比较一个操作的前后的两个或多个快照,可以检测内存泄漏。
  3. Containment 视图- 展示了你的app对象架构的整体视图 + DOMWindow
    对象(全局对象下的), GC 根部, 本地对象 (来自浏览器)。
  4. Dominators 视图- 展示了
    dominators
    树的堆图。

点击了解更多 Heap
profiler。

数据库性能

##专业小贴士

  1. 使用CSS
    Stats保证页面中完全没有未被用到的元素,唯一的样式和字体等等。
  2. Pagespeed Insight Chrome
    插件.
  3. Tag Counter Chrome
    插件.

DOM泄漏

对DOM元素的引用会导致DOM泄漏并且阻碍自动垃圾回收(GC)的进行。

来看一个例子

XHTML

<div> <div id=”container”> <h1 id=”heading”>I am just
a heading nothing much</h1> </div> </div>

1
2
3
4
5
<div>
    <div id="container">
        <h1 id="heading">I am just a heading nothing much</h1>
    </div>
</div>

JavaScript

var parentEle = document.getElementById(‘container’); //get parent ele
reference 得到父元素的引用 var headingEle =
document.getElementById(‘heading’); //get child ele reference
得到子元素的引用 parentEle.remove(); //removes parent element from DOM
从DOM中移除父元素 //but its child ref still exist, So parentEle won’t
collect GC’d and causes DOM Leak
//但是它的子元素引用仍然存在,所以parentEle不会被GC回收,因此导致了DOM泄漏。

1
2
3
4
5
6
7
8
var parentEle = document.getElementById(‘container’); //get parent ele reference 得到父元素的引用
 
var headingEle = document.getElementById(‘heading’); //get child ele reference 得到子元素的引用
 
parentEle.remove(); //removes parent element from DOM 从DOM中移除父元素
 
//but its child ref still exist, So parentEle won’t collect GC’d and causes DOM Leak
//但是它的子元素引用仍然存在,所以parentEle不会被GC回收,因此导致了DOM泄漏。

将它的引用设置为null即可修复DOM泄漏。

JavaScript

headingEle = null; //Now parentEle will be GC’d

1
headingEle = null; //Now parentEle will be GC’d

上面就是前端开发者常遇到的问题。今天就讲到这。如果你喜欢我的文章,请分享或者在下面评论。谢谢!!

2 赞 6 收藏
评论

路由转发

2. 渲染阻塞的JavaScript

如果在解析HTML标记时,浏览器遇到了JavaScript,解析会停止。只有在该脚本执行完毕后,HTML渲染才会继续进行。所以这阻塞了页面的渲染。

为了解决它

在<script></script>标签中使用 async或defer特性。

  1. <script
    async>将会在HTML解析时下载该文件并在下载完成后马上执行。
  2. <script defer>
    将会在HTML解析式下载该文件并在HTML解析完成后执行。

例如: async and defer都在Google Analytics中使用

点击查看async 和defer的浏览器支持。

关于作者:鸭梨山大

奥门威斯 21

Life hacker
个人主页 ·
我的文章 ·
14

奥门威斯 12

浏览器处理能力

内存泄漏

内存泄漏和页面臃肿 是前端开发者所要面对的问题之一。让我们来看看如何发现并解决内存泄漏。

在JavaScript中寻找内存泄漏

使用Chrome Task
Manager(任务管理器)去检测app所使用的内存以及js内存(总体内存+实时内存)。如果你的内存一直随着你的每次操作而提高,那么你可以怀疑有内存泄漏了。

下面是Chrome Task Manager的截图。

奥门威斯 19

早在2006年,雅虎就发布了提升站点性能的指南,Google也发布了类似的指南。而且有很多工具可以和浏览器一起工作,对你的Web页面的加载速度进行评估:分析页面中资源的数量,传输是否采用了压缩,JS、CSS是否进行了精简,有没有合理的使用缓存等等。

Chrome DevTools分析

使用 Heap Profiler 去查看内存泄漏。打开Chrome devTools 然后点击profiles
标签,接着选中 take heap snapshot。如果你不了解Chrome
DevTools,请阅读之前的文章.

奥门威斯 20

Heap Profiler有四个快照视图(snapshot view)

  1. Summary 视图 –
    展示对象的总体数量以及它们的实例总数,浅部(Shallow)大小(对象本身的内存大小)以及保留(Retained)大小(自动GC发生后所释放的内存大小+无法执行到的对象的内存大小)。
  2. Comparison 视图-
    用于比较一个操作的前后的两个或多个快照,可以检测内存泄漏。
  3. Containment 视图- 展示了你的app对象架构的整体视图 + DOMWindow
    对象(全局对象下的), GC 根部, 本地对象 (来自浏览器)。
  4. Dominators 视图-
    展示了 dominators 树的堆图。

点击了解更多 Heap
profiler。

如果你需要将这个过程与CI集成在一起,来对应用的性能进行监控,我去年写过一篇相关的博客。

DOM泄漏

对DOM元素的引用会导致DOM泄漏并且阻碍自动垃圾回收(GC)的进行。

来看一个例子

1
2
3
4
5
<div>
    <div id="container">
        <h1 id="heading">I am just a heading nothing much</h1>
    </div>
</div>
1
2
3
4
5
6
7
8
var parentEle = document.getElementById('container'); //get parent ele reference 得到父元素的引用
 
var headingEle = document.getElementById('heading'); //get child ele reference 得到子元素的引用
 
parentEle.remove(); //removes parent element from DOM 从DOM中移除父元素
 
//but its child ref still exist, So parentEle won't collect GC'd and causes DOM Leak
//但是它的子元素引用仍然存在,所以parentEle不会被GC回收,因此导致了DOM泄漏。

将它的引用设置为null即可修复DOM泄漏。

1
headingEle = null; //Now parentEle will be GC'd

上面就是前端开发者常遇到的问题。今天就讲到这。如果你喜欢我的文章,请分享或者在下面评论。谢谢!!

本文打算从另一个角度来尝试加速页面的渲染:浏览器是如何工作的,要将一个页面渲染成用户可以看到的图形,浏览器都需要做什么,哪些过程比较耗时,以及如何避免这些过程(或者至少以更高效的方式)。

页面是如何被渲染的

说到性能优化,规则一就是:

If you can’t measure it, you can’t improve it. – Peter Drucker

根据浏览器的工作原理,我们可以分别对各个阶段进行度量。

奥门威斯 25

图片来源:http://dietjs.com/tutorials/host\#backend

像素渲染流水线

下载HTML文档

解析HTML文档,生成DOM

下载文档中引用的CSS、JS

解析CSS样式表,生成CSSOM

将JS代码交给JS引擎执行

合并DOM和CSSOM,生成Render Tree

根据Render Tree进行布局layout(为每个元素计算尺寸和位置信息)

绘制(Paint)每个层中的元素(绘制每个瓦片,瓦片这个词与GIS中的瓦片含义相同)

执行图层合并(Composite Layers)

使用Chrome的DevTools –
Timing,可以很容易的获取一个页面的渲染情况,比如在Event
Log页签上,我们可以看到每个阶段的耗时细节(清晰起见,我没有显示Loading和Scripting的耗时):

奥门威斯 26

上图中的Activity中,Recalculate
Style就是上面的构建CSSOM的过程,其余Activity都分别于上述的过程匹配。

应该注意的是,浏览器可能会将Render
Tree分成好几个层来分别绘制,最后再合并起来形成最终的结果,这个过程一般发生在GPU中。

Devtools中有一个选项:Rendering – Layers
Borders,打开这个选项之后,你可以看到每个层,每个瓦片的边界。浏览器可能会启动多个线程来绘制不同的层/瓦片。

奥门威斯 27

Chrome还提供一个Paint Profiler的高级功能,在Event
Log中选择一个Paint,然后点击右侧的Paint
Profiler就可以看到其中绘制的全过程:

奥门威斯 28

你可以拖动滑块来看到随着时间的前进,页面上元素被逐步绘制出来了。我录制了一个我的知乎活动页面的视频,不过需要翻墙。

视频地址:https://youtu.be/gley7VZFx\_I

常规策略

为了尽快的让用户看到页面内容,我们需要快速的完成DOM+CSSOM – Layout –
Paint – Composite
Layers的整个过程。一切会阻塞DOM生成,阻塞CSSOM生成的动作都应该尽可能消除,或者延迟。

在这个前提下,常见的做法有两种:

分割CSS

对于不同的浏览终端,同一终端的不同模式,我们可能会提供不同的规则集:

@media print {
  html {
      font-family: 'Open Sans';
      font-size: 12px;
  }
}

@media orientation:landscape {
  //
}

如果将这些内容写到统一个文件中,浏览器需要下载并解析这些内容(虽然不会实际应用这些规则)。更好的做法是,将这些内容通过对link元素的media属性来指定:

<link href="print.css" rel="stylesheet" media="print">
<link href="landscape.css" rel="stylesheet" media="orientation:landscape">

这样,print.css和landscape.css的内容不会阻塞Render
Tree的建立,用户可以更快的看到页面,从而获得更好的体验。

高效的CSS规则

CSS规则的优先级

很多使用SASS/LESS的开发人员,太过分的喜爱嵌套规则的特性,这可能会导致复杂的、无必要深层次的规则,比如:

#container {
  p {
      .title {
          span {
              color: #f3f3f3;
          }
      }
  }
}

在生成的CSS中,可以看到:

#container p .title span {
  color: #f3f3f3;
}

而这个层次可能并非必要。CSS规则越复杂,在构建Render
Tree时,浏览器花费的时间越长。CSS规则有自己的优先级,不同的写法对效率也会有影响,特别是当规则很多的时候。这里有一篇关于CSS规则优先级的文章可供参考。

使用GPU加速

很多动画都会定时执行,每次执行都可能会导致浏览器的重新布局,比如:

@keyframes my {
  20% {
      top: 10px;
  }

  50% {
      top: 120px;
  }

  80% {
      top: 10px;
  }
}

这些内容可以放到GPU加速执行(GPU是专门设计来进行图形处理的,在图形处理上,比CPU要高效很多)。可以通过使用transform来启动这一特性:

@keyframes my {
  20% {
      transform: translateY(10px);
  }

  50% {
      transform: translateY(120px);
  }

  80% {
      transform: translateY(10px);
  }
}

异步JavaScript

我们知道,JavaScript的执行会阻塞DOM的构建过程,这是因为JavaScript中可能会有DOM操作:

var element = document.createElement('div');
element.style.width = '200px';
element.style.color = 'blue';

body.appendChild(element);

因此浏览器会等等待JS引擎的执行,执行结束之后,再恢复DOM的构建。但是并不是所有的JavaScript都会设计DOM操作,比如审计信息,WebWorker等,对于这些脚本,我们可以显式地指定该脚本是不阻塞DOM渲染的。

<script src="worker.js" async></script>

带有async标记的脚本,浏览器仍然会下载它,并在合适的时机执行,但是不会影响DOM树的构建过程。

首次渲染之后

在首次渲染之后,页面上的元素还可能被不断的重新布局,重新绘制。如果处理不当,这些动作可能会产生性能问题,产生不好的用户体验。

访问元素的某些属性

通过JavaScript修改元素的CSS属性

在onScroll中做耗时任务

图片的预处理(事先裁剪图片,而不是依赖浏览器在布局时的缩放)

在其他Event Handler中做耗时任务

过多的动画

过多的数据处理(可以考虑放入WebWorker内执行)

强制同步布局/回流

元素的一些属性和方法,当在被访问或者被调用的时候,会触发浏览器的布局动作(以及后续的Paint动作),而布局基本上都会波及页面上的所有元素。当页面元素比较多的时候,布局和绘制都会花费比较大。

通过Timeline,有时候你会看到这样的警告:

奥门威斯 29

比如访问一个元素的offsetWidth(布局宽度)属性时,浏览器需要重新计算(重新布局),然后才能返回最新的值。如果这个动作发生在一个很大的循环中,那么浏览器就不得不进行多次的重新布局,这可能会产生严重的性能问题:

for(var i = 0; i < list.length; i++) {
  list[i].style.width = parent.offsetWidth + 'px';
}

正确的做法是,先将这个值读出来,然后缓存在一个变量上(触发一次重新布局),以便后续使用:

var parentWidth = parent.offsetWidth;
for(var i = 0; i < list.length; i++) {
  list[i].style.width = parentWidth + 'px';
}

CSS样式修改

布局相关属性修改

修改布局相关属性,会触发Layout – Paint – Composite
Layers,比如对位置,尺寸信息的修改:

var element = document.querySelector('#id');
element.style.width = '100px';
element.style.height = '100px';

element.style.top = '20px';
element.style.left = '20px';

绘制相关属性修改

修改绘制相关属性,不会触发Layout,但是会触发后续的Paint – Composite
Layers,比如对背景色,前景色的修改:

var element = document.querySelector('#id');
element.style.backgroundColor = 'red';

其他属性

除了上边的两种之外,有一些特别的属性可以在不同的层中单独绘制,然后再合并图层。对这种属性的访问(如果正确使用了CSS)不会触发Layout

  • Paint,而是直接进行Compsite Layers:

    transform

    opacity

transform展开的话又分为: translate, scale,
rotate等,这些层应该放入单独的渲染层中,为了对这个元素创建一个独立的渲染层,你必须提升该元素。

可以通过这样的方式来提升该元素:

.element {
  will-change: transform;
}

CSS 属性 will-change
为web开发者提供了一种告知浏览器该元素会有哪些变化的方法,这样浏览器可以在元素属性真正发生变化之前提前做好对应的优化准备工作。

当然,额外的层次并不是没有代价的。太多的独立渲染层,虽然缩减了Paint的时间,但是增加了Composite
Layers的时间,因此需要仔细权衡。在作调整之前,需要Timeline的运行结果来做支持。

还记得性能优化的规则一吗?

If you can’t measure it, you can’t improve it. – Peter Drucker

下面这个视频里可以看到,当鼠标挪动到特定元素时,由于CSS样式的变化,元素会被重新绘制:

视频地址:https://youtu.be/c6wKfDpbwu8

CSS
Triggers是一个完整的CSS属性列表,其中包含了会影响布局或者绘制的CSS属性,以及在不同的浏览器上的不同表现。

总结

了解浏览器的工作方式,对我们做前端页面渲染性能的分析和优化都非常有帮助。为了高效而智能的完成渲染,浏览器也在不断的进行优化,比如资源的预加载,更好的利用GPU(启用更多的线程来渲染)等等。

另一方面,我们在编写前端的HTML、JS、CSS时,也需要考虑浏览器的现状:如何减少DOM、CSSOM的构建时间,如何将耗时任务放在单独的线程中(通过WebWorker)。

发表评论

电子邮件地址不会被公开。 必填项已用*标注