web性能优化

写在前面

在写这篇文章以前在个人做过的优化基础和认知基础上,查阅了众多文章。毕竟要写个什么就得认真点弄,这些文章在最后将会一一指出出处。性能优化是个很广泛的命题,这点必须承认。

前端作为网站的面子工程建设者,很大程度上承担了性能优化的重担,但是这不是全部,网站的性能优化做好了前端的优化之后,优化方向就慢慢跑到底层去了——比如:HTML&JS&CSS&IMG->HTTP->TCP->DNS 这个路径。

写到这个路径这里,我也算是有个写作路径了,就按这个路径来写。

优化纬度

HTML&JS&CSS&IMG优化

HTML&JS&CSS&IMG这个层面上东西不少,核心的一条里面就有体积。因为体积决定了速度。不过这些都不算全部,一个个整理一下。

个人以为:HTML&JS&CSS&IMG这个层面上的优化绝对是在考验一个前端基本的修养,这是本职。其他方面的东西,可以算是一个前端的综合面面观吧。

HTML

HTML据说可以优化的地方很多,比如这里的介绍: 飞机,但是就个人做过的优化体验来说HTML上面可以玩的文章不算多。这里按权重到低列举一下:

  1. 压缩: 前端优化说穿了就一句:速度。体积是速度的第一道保障。选一个好的压缩工具然后压缩pre之外的一切是不会错的。
  2. CSS置顶和JS置尾: css置顶可以更快渲染页面,页面不会载入初期出现无样式的难看样子,置尾JS这个一来为了更快渲染DOM防止js载入阻塞了HTML,二来是多数JS代码依赖DOMReady,过早载入js会导致部分js执行报错、失效。
  3. 外部js和css: 这个有利于充分利用缓存,使得js和css不会因为html变化失效重新请求占用带宽和时间。
  4. 指定图片大小: 可以减少页面的重排。
  5. 使用相对URL: 使用相对路径的好处有几个,一个是可以减少路径长度,一个则是对工程化有非常大的便利程度,相对路径有利于自动化工具找到引用的文件。
  6. 删除注释: 这个似乎不用说太多。更少的注释代表更少的带宽和更快的速度,不过大多数时候这个无足重轻,注释在HTML中个人认为占用不会达到1%。不过这实在一个很容易做到的优化措施。
  7. 减少HTML标签嵌套深度: PC端这不是一个问题,但是在移动端这是一个行之有效的优化方案。

JS

早期项目不大要求不高时候JS效率差点其实无所谓,因为js根本就没多少行。但是随着越来越多的前端主导团队出现,SPA的盛行,项目js早就开始实现从KB向MB跨越了。还是托了GZIP的福,让那些动不动上MB的JS可以压缩到200K以内,要不然很多很多项目根本过不了上线审核。

言归正传,现在说说JS相关,JS性能优化有很多,但是这里只列举一些比较重要的。依旧按按权重到低列举:

  1. 压缩: 至少在网速没有实现质的突破和移动流量变得廉价之前,优化的权重个人认为首推的还是体积。更小体积就是更快的速度,js压缩一遍再上线应当作为必经道路
  2. combo: 这玩意是说合并。每个请求背后都经历着DNS解析->TCP三次握手->HTTP传输的路径,压缩可以显著减少重复和浪费,尤其是一些早期单个页面引入JS数量达到10+的项目。
  3. 减少操作DOM:
    • 缓存已经访问过的元素;
    • 使用 DocumentFragment 暂存 DOM,整理好以后再插入 DOM 树;
    • 操作 className,而不是多次读写 style;
    • 避免使用 JavaScript 修复布局。
  4. ajax缓存: Make AJAX cacheable是YSlow一个检测项目。Optimizing AJAX responses is important to improve performance(优化ajax请求对提高性能是重要的)。ajax更多是304协商缓存,但是个人认为已经足够。至于200(from cache)强缓存嘛,如果服务端做好Cache-Control或者Expires配置,那更是极好的。 使用这个措施,jQuery的ajaxSetup就要打开缓存了。
  5. 高效的事件处理:
    • 减少绑定事件监听的节点,如通过事件委托;
    • 尽早处理事件,在 DOMContentLoaded 即可进行,不用等到 load 以后。
    • 节流函数限制mousemove,scroll等触发频次太高的事件触发次数
  6. Ajax通过GET发出: Use GET for AJAX requests同样是是YSlow一个检测项目。
    • POST发Ajax请求需要两步:1.发送http头文件 2.发送数据
    • GET发Ajax仅有一步,但是会带上cookie。YSlow说IE的url长度最大2kb,这时注意不能用GET——不过通过浏览器地址栏输入地址那叫做Ajax吗?这点我有时间测试一下(TODO)。
  7. JS并行加载: jQuery走过来的人肯定遇到过js顺序换个就报错的事情,这是因为HTML从上到下执行,一个js载入完成之前不会载入它下面的js(下载过程中会阻塞下面的下载和渲染)。如果js过多的话这就会造成过多的时间被白白浪费。但是一定的技术方案是可以同时实现依赖和并行下载的问题的,比如Lab.js。

CSS

CSS研究的不深入,下面是自己可以想到的全部了:

  1. 压缩: 减少体积
  2. 合并: 减少HTTP
  3. 雪碧图: 减少HTTP
  4. 拆分&按需加载: 尽量减少无用的css
  5. 选择器优化: 研究的不深入就不献丑了,个人认为大头是层级嵌套这类。

IMG

  1. 不用图片: 体积的节省不如直接不用,很多简单的图可以用css实现的就别用图片了。另外SVG也可以用来替代图片了。
  2. 优化图片格式: 比如说:webP。这个可以视为压缩的更优方案。更小体积更多细节。
  3. 压缩: 这个不用说吧?
  4. 合并 雪碧图,不要问我雪碧图是啥
  5. 合适的尺寸: 不要用100*100的图缩放成10*10的
  6. 多域名: 浏览器对单个域名下的请求并发数是有限制的(参考TCP下的图表,GO),如果是图片量很多的页面,需要考虑使用多域名。这个可以参考百度图片

图片优化无关但是和图片有关的:

  1. 懒加载
  2. Gzip
  3. CDN
  4. 缓存

HTTP优化

http优化这块呢,就一个原则吧:
越少越好,越小越好,越快越好

越少越好

这个就是要减少请求数量了。
主要是下么几个方面:

  • js&css&img合并: 这个在上面已经有提到。
  • 做好强缓存方案: 强缓存(200 from cache)是当代web优化的重中之重吧。为了实现这个目的,一定要配置好响应头的Cache-Control&Expires(可以参考上一篇《错配的nginx——web缓存问题探究》)。如果不写这个到响应头,很多坑比的事情会找上你。
  • Ajax缓存: 做好服务端优化这个不少地方也是可以实现强缓存而不需要多次请求的。
  • 正确设置src属性 空的src属性,会被浏览器指向前页面所在目录。

越小越好

  • js&css&img压缩: 压缩能减少体积这个地球人都知道。
  • Gzip 这个东西嘛,说实话,多数时候比你进行js&css&img压缩更加简单高效。Gzip绝对是重量级的东西(200 from cache强缓存也是重量级的东西,他们一个从根源上解决请求,一个解决请求中的传输大小,做好这两个往往比你费尽心思做的其他所有的优化更加管用&省事)

越快越好

就三个字母:CDN

TCP优化

  1. 同一域名下的TCP连接的限制: CDN和单独的图片空间和避开这个限制提高并行下载速度,但是过多域名会造成DNS解析时间的开销。
  2. 一定要减少404: 上面说了TCP限制的事情,,而404本身就会占用一个TCP链接。这就和有数几条运输公路堵了1条一样,如果404更多的话,那岂不是就三四条路动不动堵车一样。。。

补充: 下面是网上找到的TCP数量资料(Roundup on Parallel Connections),数据不是很新,但是可以参考一下。

BrowserHTTP/1.1HTTP/1.0
IE 6,724
IE 866
Firefox 366
Safari 3,444
Chrome 4+6?
iPhone 44?
Opera 10.51+8?

DNS优化

DNS优化说实话个人没有做过,不过写到这里还是去了解了一下:
一般来说,在前端优化中与 DNS 有关的有两点:一个是减少DNS的请求次数,另一个就是进行DNS预获取。

减少请求数量

减少请求数量这个东西其实和提升TCP并发是相爱相杀的关系。这表现在:

  • 越少的域名代表越少的DNS请求
  • 越多的域名代表着越多的并发
  • 而我们想要的是:更少的DNS请求和更多并发

这样就没得整了,大多数方案都是整个平衡,将域名限制在2-4之间。
然而淘宝跳出来说:我有钱我任性,我的资产一个域名装不下:

好吧,淘宝牺牲了DNS访问请求来保证并发了,好了,你现在知道淘宝为什么要做 阿里DNS了吧?相应的,还有”马化腾DNS”和”李寥宏DNS”,你们现在知道为嘛要有这个吗?

不过除了自建DNS服务器,淘宝还在首页用到了DNS Prefetch,就是下面这货,算是取了个很好的平衡。

预获取

淘宝网站首页用到了这个DNS优化:

1
2
3
<link rel="dns-prefetch" href="//g.tbcdn.cn">
<link rel="dns-prefetch" href="//g.alicdn.com">
<link rel="dns-prefetch" href="//tce.alicdn.com">

这个就是预先获取的实现方式了,具体的可以点参考文章了解更多,或者直接这里:1-2-3——走你!

DNS缓存

DNS也是有缓存,它定义了DNS在服务器上的存活时间。
当地址栏敲下网址按下Enter,浏览器就开始使用DNS来进行网址解析,这个路径往往是这样的浏览器缓存->本机缓存->路由器缓存->ISP DNS->根服务器递归搜索。

浏览器DNS缓存的DNS和TTL无关,这个一般看浏览器厂商的倾向了,Chrome的过期时间是1分钟。
本机缓存不会完全等于这个TTL 它会参考但是不完全等于TTL值。
ISP的缓存要看ISP心情和良心,有些缓存服务器(不多)会忽略网站DNS提供的TTL,自己设置一个较长的TTL。

所以总体来说,较长的TTL有利于提高网页打开速度,这个速度是全局性质的,因为所有的速度都必须加上DNS解析时间,积少成多这个时间就非常可观了。举个例子说你网站就算快成光不要时间传输渲染,但是DNS解析要10秒,照样体验一团糟。

所以说较长的TTL会直接有利于提高网站打开速度,这个速度是基础级别的,你的网络DNS解析时间有多长,这个优化相应的就会多有效。一般的域名TTL从20分钟到数小时都是很常见的。如果你不考虑灾备的话可以将这个TTL设为一天或者一周。但是这也意味一旦你的网站服务器挂了你无法快速恢复,比如被人格式化了硬盘,那么你的服务就必然会中断一天或者一周。

这项优化必须要谨慎再谨慎。毕竟灾难可能随时到来。

总结

到这里算是词穷了。目前个人理解的优化基本就到此为止了。不过最后还是做个总结吧,优化的主要路径:

  1. 200(from cache)强缓存、304协商缓存和Gzip,这个一定要做,所谓web优化,我认为5成的比重在这里。
  2. HTML+JS+CSS压缩、按需加载,雪碧图,这个也必须要做,我认为它占到优化比重的3成。
  3. 最后2成嘛,多域名,高TCP并发,CDN,自建DNS,服务器集群,这些就要靠钱来砸了。

就这样了,如果有错误,欢迎大家指出。

参考

《不应忽视的HTML优化》
《前端性能优化最佳实践》
《Web前端性能优化实践》
知乎问答
《前端优化系列之一:DNS预获取 DNS Prefetch 提升页面载入速度》
《浏览器、操作系统DNS缓存时间》