页面呈现过程中的前端性能优化点
前端性能优化是使 页面 更快、更好、无错误、用户操作更流畅、体验更好。
从用户输入URL到页面整个呈现在用户眼前,还有用户交互的过程都是性能优化的切入点。
浏览器请求过程
当浏览器请求一个URL的时候,会经过以下过程:阻挡、域名解析、建立连接、发送请求、等待响应、接收数据。
阻挡:不同浏览器对于单个域名的最大并发连接数有一定的限制,如果浏览器同时对某个域名发起多个请求,超多限制就会出现等待,也就是阻挡。
和并发资源数有关的性能优化技术:
domain hash, cookie free, css sprites, js/css combine, max expires time, loading images on demand
1、cookie free
启用和主站不同的域名来放置静态资源,减少不必要的cookie发送
按照普通设计,当网站cookie信息有1 KB、网站首页共150个资源时,用户在请求过程中需要发送150 KB的cookie信息,在512 Kbps的常见上行带宽下,需要长达3秒左右才能全部发送完毕。 尽管这个过程可以和页面下载不同资源的时间并发,但毕竟对速度造成了影响。 而且这些信息在js/css/images/flash等静态资源上,几乎是没有任何必要的。 解决方案是启用和主站不同的域名来放置静态资源,也就是cookie free。
2、domain hash
由于浏览器对于单个域名的并发数限制,可以使用多个域名加大浏览器并发量
将css放置在页面最上方应该是很自然的习惯,但第一个css内引入的图片下载是有可能堵塞后续的其他js的下载的。而在目前普遍过百的整页请求数的前提下,浏览器提供的仅仅数个并发,对于进行了良好优化甚至是前面有CDN的系统而言,是极大的性能瓶颈。 这也就衍生了domain hash技术来使用多个域名加大并发量(因为浏览器是基于domain的并发控制,而不是page),不过过多的散布会导致DNS解析上付出额外的代价,所以一般也是控制在2-4之间。 这里常见的一个性能小坑是没有机制去确保URL的哈希一致性(即同一个静态资源应该被哈希到同一个域名下),而导致资源被多次下载。
3、css sprites
通过使用css sprites减少请求总数。
再怎么提速,页面上过百的总资源数也仍然是很可观的,如果能将其中一些很多页面都用到的元素如常用元素如按钮、导航、Tab等的背景图,指示图标等等合并为一张大图,并利用css background的定位来使多个样式引用同一张图片,那也就可以大大的减少总请求数了,这就是css sprites的由来。
4、js/css combine
合并js、css文件减少浏览器的重新渲染
压缩资源减小传输的体积
全站的js/css原本并不多,其合并技术的产生却是有着和图片不同的考虑。 由于cs/js通常可能对dom布局甚至是内容造成影响,在浏览器解析上,不连贯的载入是会造成多次重新渲染的。因此,在网站变大需要保持模块化来提高可维护性的前提下,js/css combine也就自然衍生了,同时也是minify、compress等对内容进行多余空格、空行、注释的整理和压缩的技术出现的原因。
5、max expires time
静态资源缓存有效期加长
随着cookie free和domain hash的引入,网站整体的打开速度将会大大的上一个台阶。 这时我们通常看到的问题是大量的请求由于全站公有header/footer/nav等关系,其对应文件早已在本地缓存里存在了,但为了确保这个内容没有发生修改,浏览器还是需要请求一次服务器,拿到一个304 Not Modified才能放心。 一些比较大型的网站在建立了比较规范的发布制度后,会将大部分静态资源的有效期设置为最长,也就是Cache-Control max-age为10年。 这样设置后,浏览器就再也不会在有缓存的前提下去确认文件是否有修改了。 超长的有效期可以让用户在访问曾访问过的网站或网页时,获得最佳的体验。 带来的复杂性则体现在每次对静态资源进行更新时,必须发布为不同的URL来确保用户重新加载变动的资源。
6、loading images on demand
图片按需加载,用占位符填补图片空位,待图片进入可视区域再进行加载
即使是这样做完,仍然还存在着一个很大的优化空间,那就是很多页面浏览量很大,但其实用户直接很大比例直接就跳走了,第一屏以下的内容用户根本就不感兴趣。 对于超大流量的网站如淘宝、新浪等,这个问题尤其重要。 这个时候一般是通过将图片的src标签设置为一个loading或空白的样式,在用户翻页将图片放入可见区或即将放入可见区时再去载入。 不过这个优化其实和并发资源数的关系就比较小了,只是对一些散布不合理,或第一页底部的资源会有一定的帮助。 主要意图还是降低带宽费用。
资源加载
浏览器在加载html时,只要网络层返回一部分数据后就会开始解析,遇到外链样式、脚本、图片都会像服务器请求资源,而不需要等到所有html都下载完成才开始,渲染也是同步进行的。
1、将css放在html的头部可以随html的加载一起渲染页面,减少页面空白时间,更快的让用户看到渲染的页面。
2、js的下载和解析会阻塞其他的下载和呈现,所以尽量将js在页面底部。
3、延迟加载js文件。
4、按需加载资源。
浏览器渲染页面
渲染过程
浏览器渲染引擎的渲染过程,主要有三步骤:
1、解析。浏览器会解析HTML/SVG/XHTML,事实上,webkit有三个C++的类对应这三类文档。浏览器解析这三种文件会产生一个DOM Tree;解析CSS,产生style rules;解析Javacript,主要通过DOM API和CSSOM API来操作DOM Tree和CSS Rule Tree
2、浏览器引擎通过DOM Tree和CSS Rule Tree来构造Rendering Tree。
Rendering Tree 并不与 DOM Tree 对应,比如像
通过 CSS Rule Tree 匹配 DOM Tree 进行定位坐标和大小,是否换行,以及 position、overflow、z-index 等等属性,这个过程称为 Flow 或 Layout 。
3、最终通过调用Native GUI 的 API 绘制网页画面的过程称为 Paint。
重绘和重排
当用户在浏览网页时进行交互或通过 js 脚本改变页面结构时,以上的部分操作有可能重复运行,此过程称为 Repaint 或 Reflow。
Repaint
当元素改变的时候,将不会影响元素在页面当中的位置(比如 background-color, border-color, visibility),浏览器仅仅会应用新的样式重绘此元素,此过程称为 Repaint。
Reflow
当元素改变的时候,将会影响文档内容或结构,或元素位置,此过程称为 Reflow。( HTML 使用的是 flow based layout ,也就是流式布局,所以,如果某元件的几何尺寸发生了变化,需要重新布局,也就叫 Reflow。)
Reflow 的成本比 Repaint 的成本高得多的多。一个结点的 Reflow 很有可能导致子结点,甚至父点以及同级结点的 Reflow 。在一些高性能的电脑上也许还没什么,但是如果 Reflow 发生在手机上,那么这个过程是延慢加载和耗电的。—-浏览器的渲染原理简介
- 当你增加、删除、修改DOM结点时,会导致Reflow或Repaint
- 当你移动DOM的位置,或是搞个动画的时候。
- 当你修改CSS样式的时候。
- 当你Resize窗口的时候(移动端没有这个问题),或是滚动的时候。
- 当你修改网页的默认字体时。
针对渲染的优化方法
1、减少 CSS 嵌套层级和选择适当的选择器(CSS匹配DOM Tree主要是从右到左解析CSS的Selector)
2、不要通过 JS 逐条修改 DOM 的样式,提前定义好 CSS 的 Class 进行操作。
3、把DOM离线后修改样式。
4、不要把DOM结点的属性值放在一个循环里当成循环里的变量。不然这会导致大量地读写这个结点的属性。
5、尽可能的为产生动画的 HTML 元素使用 fixed 或absolute 的 position ,那么修改他们的 CSS 是不会 Reflow 的。
PageSpeed
PageSpeed Insights是Google的一个网站性能测试工具,可以根据这十大规则来进行更多性能上的优化。
最后
越来越重视用户体验的现在,性能优化也是前端比较重要的点,并且还有根据用户需求来做的优化,比如优先加载首屏、优先加载文章内容等… 在秒甚至毫秒级的时间里偷时间,这也是前端的乐趣所在。
最近正在读的性能优化的好书:
《Web性能实践日志》