web性能优化浅析

HTTP头部

压缩头部

如果浏览器和服务器都支持的话,可以使用压缩来减小响应的大小。

请求头: Accept-Encoding(浏览器可以支持的web服务器返回内容压缩编码类型): gzip, deflate

响应头:Content-Encoding: gzip

条件get请求

听起来很高大上的样子,其实就是在发送get请求的时候加上一个If-Modified-Since字段,这个时候是属于协商缓存的,服务器会根据If-Modified-Since中的时间和Last-Modified的时间来决定是否返回304.这个时候服务器不再发送响应体(返回的消息就只有响应头??)。

Expires

如果响应头中包含expires字段,就是用的强制缓存(cache-control 和expires)了,在客户端进行判断,只要没有过期就会使用缓存版本,而不会进行任何HTTP请求。

Keep-Alive

connection:可以取值为close或者keep-alive

  1. 在HTTP 1.0中,客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。因此如果要请求一个HTML文件,其中包含10张图片就要产生11个TCP连接。当然多个TCP连接可以并行,但是默认的情况下大部分浏览器打开5~10个并行的TCP连接。
  2. 在HTTP 1.1中则可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求。和1.0中不同的是,多个请求可以共用一个TCP连接,而不用每次都是请求–响应–释放。
    image

再说下HTTP2.0中的多路复用问题,事实上,1.1中的长连接每个请求是依次进行的,但是2.0中不是这样。
image

优化方案

1. 减少HTTP请求数量

事实上,解析一个网页只有10%–20%左右的时间花在请求html文档上,剩余80%~90%的时间花在请求html文档所用到的组件上,包括图片/js/css

图片地图

将导航栏的多个图标用一张图片代替,根据用户点击的位置跳到指定的链接。分为服务器端图片地图和客户端图片地图

雪碧图

根据background-position来设置

内联图片

内联图片不会产生http请求,允许小块数据内联为‘立即数’,数据就在url自身当中。

格式:

1
data:[<mediatype>][;base64],<data

缺点:

  1. 浏览器不会缓存内联图片资源
  2. IE8以下不支持
  3. 超过100KB的图片,base64编码会使图片大小增大,导致网页整体下载速度减慢

合并脚本和样式表

具体应该用到什么工具呢?如果最后合成一个资源,那减少了HTTP请求和每个页面下载了不需要的代码,该如何取舍呢?

2.使用内容分发网络(CDN)

3.使用expires

expires前面已经有提到了,http1.1中引入了cache-control字段,主要原因是expires的过期时间是一个绝对时间。因此要求服务器和客户端的时间基本上要保持同步。

4. 压缩组件

前面提到过,在请求头中添加Accept-Encoding可以设置浏览器支持的web服务器返回内容压缩编码类型。服务器在响应头中添加Content-Encoding。gzip是最流行和最有效的压缩方法。常用的还有deflate。

那我们应该压缩哪些内容呢?html css js以及响应的xml和json文本都是可以压缩的。图片和pdf不应该压缩。

代理缓存问题

想像一个场景,当浏览器通过代理来发送请求的情况下。假设针对某个url的第一个请求是不支持压缩的,所以服务器的响应肯定也是未压缩的,因此代理的缓存也是未压缩的。但是当第二个请求来自于一个支持压缩的浏览器的访问,那么代理还是会以之前缓存的未压缩的进行响应。反之,当第一个请求来自于一个支持压缩的浏览器,但是第二个请求来自于不支持压缩的浏览器,情况也就更加严重了。

解决方案:在响应中添加vary:Accept-Encoding,服务器根据一个或多个请求头来改变缓存的响应。这样代理就可以根据Accpet-Encoding的值的不同,缓存多个响应版本。

5. 将样式表放在顶部

白屏

这里不得不说一句的是,如果将css放在底部或者是在head中以@import的方式引入样式表,都有可能会导致白屏现象,然后所有内容同时涌上屏幕。

无样式内容闪烁(FOUC)

出现无样式现象是图片或其他的内容已经显示了,但是当css样式加载完了以后又重新渲染显示。原因同样是是因为将css放在了底部。

白屏和FOUC的选择:如果将css放在底部,浏览器有两种选择,白屏或者FOUC。如果css仍在加载,构建和呈现树就是一种浪费—-白屏。逐步显示,css加载完了再更新—FOUC

所以将样式表放在底部可能会导致白屏和FOUC(Flash of unstyled content)

6. 将脚本放在底部

在下载解释执行js文件的时候,是不能下载其他的图片资源以及css样式的。

在下载除js的资源外,其他资源都是可以并行下载的。为什么js不能并行下载呢?

第一,多个js之间可能有依赖关系。
第二,脚本可能使用document.write来修改页面内容。

defer

正常来说脚本都是下载了然后就会解释执行,但是加上defer之后,就告诉浏览器,可以不用对我立即执行。但是,在用defer声明的脚本中,是不能出现document.write的;并且,其他脚本也是不能引用该defer脚本内的变量的。

7. 避免使用css表达式

因为css表达式会被频繁地进行计算,当页面呈现和改变,滚动,用户鼠标拖拽都会重新求值。

解决方案

  1. 一次性表达式,即在css中调用js函数。

    1
    2
    3
    4
    5
    6
    7
    p{
    background-color: expression(altBgcolor(this))
    }
    js:
    function altBgcolor(elem){
    elem.style.backgroundColor = ....
    }
  2. 或者直接在js中绑定事件函数中修改css样式。

8. 使用外部js和css

如果将所有js文件都放在html中,第一次加载,内联式的性能会优于外联式。但是外联式有一个优点是可以缓存js和css文件。

这里又不得不说一个重用的问题,应该把各个页面相同的css部分提取出来,这样请求的时候就可以从缓存中获取。同时还有一个大的优化方向,即第一条说的请求数量越少越好。

9. 减少DNS的查找

浏览器默认会将DNS和对应的IP缓存起来。但是服务器可以在返回的DNS记录中包含一个TTL(time-to-live),来表明可以被缓存的时间。但有些时候浏览器会忽略TTL而设置自己的时间限制。

所以如果浏览器支持keep-alive的话,那么一个持久的TCP链接将会一直使用,直至出现一定的空闲时间为止。(如IE的keepAliveTimeout:默认为1分钟)

10. 精简的js

注释、不必要的空白字符都会被移除。

混淆:移除注释和空白,同时将函数和变量的名字转化为更短的字符串。但是混淆可能会引入错误;维护和调试也更加困难。

常用的压缩工具:JSMin

精简CSS:合并相同的类,移除不使用的类。

11. 避免重定向

301:永久性重定向。返回301的时候通常会返回一个Location:url字段,表示需要被重定向到的url。

1
2
3
4
5
6
7
<meta http-equiv="refresh" content=0; url="...">这样也可以重定向。
ducument.location也可以重定向。
解决措施:加上结尾的‘/’,缺少斜线的话可能会导致重定向。
也可以通过将appache2中的DirectionSlash off

12. 删除重复的脚本

13. 配置或移除Etag

ETag: entity tag实体标签

ETag是为了解决if-modefied-Since的时间只能精确到s,并且可能内容并未改变,只是一些不重要的信息改变了,也会导致重新下载。

ETag的缺点:如果是在一个集群系统中,当浏览器从一台服务器中获取了原始组件之后,如果再向另一天服务器请求相同的内容,ETag是不会匹配的。

14. 使用AJAX缓存

加入cache-control字段和expires字段,来让ajax请求也支持缓存。