前端性能优化(四)02-页面性能优化之优化原则——压缩资源 & 避免3xx和4xx & AJAX优化 & Cookie优化 & 利用缓存 & 缩短服务器响应时间
优化原则
7、压缩资源
-
压缩js,css,image
通过对外部资源进行压缩可以大幅度地减少浏览器需要下载的资源量,它会减少关键路径长度与关键字节,使页面的加载速度变得更快。
对数据进行压缩其实就是使用更少的位数来对数据进行重编码。如今有非常多的压缩算法,且每一个的作用领域也各不相同,它们的复杂度也不相同,不过在这里我不会讲压缩算法的细节,感兴趣的朋友可以自己Google。
在对
HTML
、CSS
和JavaScript
这些文件进行压缩之前,还需要先进行一次冗余压缩。所谓冗余压缩,就是去除多余的字符,例如注释、空格符和换行符。这些字符对于程序员是有用的,毕竟没有格式化的代码可读性是非常恐怖的,但它们对于浏览器是没有任何意义的,去除这些冗余可以减少文件的数据量。小图片采用base64的格式,直接嵌入代码中,可以帮我们减少http请求,但是同样,这个会造成我们代码的提及变大。请求的速度会减慢,这个需要平衡。
常用工具:webpack,gulp
浏览器接收到服务器返回的HTML、CSS和JavaScript字节数据并对其进行解析和转变成像素的渲染过程被称为
关键渲染路径
。通过优化关键渲染路径即可以缩短浏览器渲染页面的时间。 -
删除重复的脚本,类似
tree-shaking
在一个页面中重复引用一个脚本可能存在的问题:浏览器会重复下载并执行脚本文件。
-
使用合适大小的图片
如果你只需要一个小图,就不要传一个大图。例如你实际需要显示的是一个60x60的头像,就不要传一个100x100的然后再通过设置宽高将它缩小为60x60的。原因很简单,这样会消耗不必要的带宽和系统资源。
-
减少DOM元素的数量
复杂页面意味着要下载更多字节,这也意味着JavaScript中的DOM访问速度更慢。如果您想要添加事件处理程序,例如,在页面上循环500或5000个DOM元素,则会有所不同。
-
使用异步加载,async、defer
服务端渲染也是一种减少浏览器资源消耗,减少页面重排重绘,也是一种现行MVVM框架单页应用最主要的SEO优化手段。
8、避免3xx/4xx
避免重定向
3xx是重定向相关的HTTP响应代码
重定向的意思是,用户的原始请求(例如请求A)被重定向到其他的请求(例如请求B)。
每次页面重定向到另一个页面时,您的访问者都会面临等待HTTP请求 - 响应周期完成的额外时间。例如,如果移动重定向模式如下所示:
example.com
- > www.example.com
- > m.example.com
- > m.example.com/home
,这两个额外重定向中的每一个都会使您的页面成为可能加载速度慢。
HTTP 重定向通过 301
/302
状态码实现。
HTTP/1.1 301 Moved Permanently
Location: http://example.com/newuri
Content-Type: text/html
301 Moved Permanently
,这个状态码标识用户所请求的资源被移动到了另外的位置,客户端接收到此响应后,需要发起另外一个请求去下载所需的资源。
302 Found
,这个状态码标识用户所请求的资源被找到了,但不在原始位置,服务器会回复其他的一个位置,客户端收到此响应后,也需要发起另外一个请求去下载所需的资源。
客户端收到服务器的重定向响应后,会根据响应头中 Location
的地址再次发送请求。重定向会影响用户体验,尤其是多次重定向时,用户在一段时间内看不到任何内容,只看到浏览器进度条一直在刷新。
有时重定向无法避免,在糟糕也比抛出 404
好。虽然通过 HTML meta refresh 和 JavaScript 也能实现,但首选 HTTP 3xx
跳转,以保证浏览器「后退」功能正常工作(也利于 SEO)。
常见的优化办法:
-
最浪费的重定向经常发生、而且很容易被忽略:URL 末尾应该添加
/
但未添加。比如,访问http://astrology.yahoo.com/astrology
将被 301 重定向到http://astrology.yahoo.com/astrology/
(注意末尾的/
)。如果使用 Apache,可以通过Alias
或mod_rewrite
或DirectorySlash
解决这个问题。 -
网站域名变更:CNAME 结合
Alias
或mod_rewrite
或者其他服务器类似功能实现跳转。 -
在定义链接地址的href属性的时候,尽量使用最完整的、直接的地址。例如:
使用www.cnblogs.com
而不是cnblogs.com
使用cn.bing.com
而不是bing.com
使用www.google.com.hk
而不是google.com
使用www.mysite.com/products/
而不是www.mysite.com/products
避免404浏览器找不到资源的情况发生
发出HTTP请求并获得无用的响应(即404 Not Found)是完全没必要的。特别糟糕的是当外部JavaScript的链接错误并且结果是404时。首先,此下载将阻止并行下载。接下来,浏览器可能会尝试解析404响应主体,就像它是JavaScript代码一样,这样就带来的性能的浪费。
404的影响:
有时候,404错误发生了,用户可能根本没有感觉到。例如
- 例如请求favicon.ico文件,或者请求了某个不存在的脚本文件、样式表、图片文件,页面还是会按照正常的方式进行呈现。
- 丢失的脚本文件、样式表、图片文件,会导致页面的某些行为、界面效果出现异常(也可能不是很明显)
- 最大的问题可能是性能方面的影响。尤其是如果请求一个不存在的脚本文件,因为浏览器在请求脚本文件的时候,即便是返回404,它也会尝试去按照Javascript的方式解析响应中的内容。这无疑会增加很多处理的时间,而因为该文件不存在,所以这些都是无用功。
看得到的影响:
- 如果用户请求的某个页面不存在,那么他将收到明确的回应
- 默认情况下,他将收到一个标准的错误页面(请注意:不少用户会被这个页面吓到)
常见的优化办法:
404 意味着Not Found,意思是说未找到资源。既然如此,那么至少会有两种原因导致404错误:
- 该资源按理说是要有,但我们没有提供。用户按照正常的方式来请求,所以资源找不到。
- 为网站提供favicon.ico这种经常可能会被忽略的资源
- 使用一些检查工具:比如Link checker
- 该资源本来就不存在,用户按照不正常的方式来请求,当然还是找不到。
- 避免用户收藏绝对地址,给后期更新带来隐患。可以使用地址Rewrite来重写,或者在设计阶段定义一些灵活友好的地址
- 使用Routing技术,配置路由规则。
9、AJAX优化
使Ajax可缓存
AJAX=Asynchronous JavaScript And XML,AJAX不是新的编程语言,而是一种使用现有标准的新方法。
AJAX是与服务器交换数据并更新部分网页的艺术,在不重新加载整个页面的情况下。
由于AJAX其实也是需要发起请求,然后服务器执行,并将结果(通常是JSON格式的)发送给浏览器进行最后的呈现或者处理,所以对于网站设计优化的角度而言,我们同样需要考虑对这些请求,是否可以尽可能的利用到缓存的功能来提高性能。
对于AJAX而言,有一些特殊性,并不是所有的AJAX请求都是可以缓存的。
POST
的请求,是不可以在客户端缓存的,每次请求都需要发送给服务器进行处理,每次都会返回状态码200。(这里可以优化的是,服务器端对数据进行缓存,以便提高处理速度)GET
的请求,是可以(并且默认)在客户端进行缓存的,除非指定了不同的地址,否则同一地址的AJAX请求,不会重复再服务器执行,而是返回304。
有的时候,我们可能希望GET
请求不被缓存,有几种做法来达到这样的目的。
- 每次调用的时候,请求不同的地址(可以在原始地址后面添加一个随机的号码)。
- 如果你所使用的是jquery的话,则可以考虑禁用AJAX的缓存。
$.ajaxSetup({ cache: false });
axios中
var config = { headers: {'Content-Type': 'application/json','Cache-Control' : 'no-cache'}};
axios.get('/post',config)
使用GET的Ajax请求
在使用XMLHttpRequest(目前的AJAX都是基于它实现的)的时候,浏览器中的POST实现为两步走的过程,首先发送头部信息,然后再发送数据。但如果是使用GET的话,就只有一个TCP的包发送出去(除非有大量的Cookie),这样无疑可以提高性能。
但是get有容量限制,大于2K(对get的限制IE是2K,firefox、chrome是4K)的内容只能用post。
10、Cookie优化
-
减小cookie大小
减小cookie的大小,因为在发请求时浏览器会将cookie信息发送到server端,所以应该只在cookie中存必要的信息且越长度越小越好。在 写cookie的时候要记得给cookie设置一个合理的过期时间及域
-
对一些静态资源不需要使用cookie的单独设置域
当浏览器发出静态图像请求并将cookie与请求一起发送时,服务器对这些cookie没有任何用处。因此,他们只会毫无理由地创建网络流量。您应该确保使用无cookie请求请求静态组件。创建一个子域并在那里托管所有静态组件。
如果您的域名是
www.example.org
,您可以托管您的静态组件static.example.org
。但是,如果您已经在顶级域上设置了cookieexample.org
而不是www.example.org
,则所有请求都static.example.org
将包含这些cookie。在这种情况下,您可以购买一个全新的域,在那里托管您的静态组件,并保持此域无cookie。雅虎 用途yimg.com
,YouTube使用ytimg.com
,亚马逊使用images-amazon.com
等。在无cookie域上托管静态组件的另一个好处是,某些代理可能拒绝缓存使用cookie请求的组件。在相关说明中,如果您想知道是否应该使用
example.org
或www.example.org
作为主页,请考虑cookie的影响。省略www会让您别无选择,只能写入cookie
*.example.org
,因此出于性能原因,最好使用www子域并将cookie写入该子域。
归类一下:
- 去除不必要的 Cookie;
- 尽量压缩 Cookie 大小;
- 注意设置 Cookie 的 domain 级别,如无必要,不要影响到 sub-domain;
- 设置合适的过期时间。
11、利用缓存
浏览器会缓存大量信息(样式表,图像,JavaScript文件等),以便当访问者返回您的站点时,浏览器不必重新加载整个页面。然后设置“expires”标题,以表示希望缓存该信息的时间。在许多情况下,除非您的网站设计经常更改,否则一年是合理的时间段。
利用浏览器缓存 ,为链接或者资源,添加Expires或Cache-Control头
- 对于静态组件:通过设置远期未来
Expires
标头实现“永不过期”策略 - 对于动态组件:使用适当的
Cache-Control
标头来帮助浏览器处理条件请求
格式:
Expires = "Expires" ":" HTTP-date
# e.g.
Expires: Thu, 01 Dec 1994 16:00:00 GMT
#(必须是GMT格式)
通过HTTP的META设置expires和cache-control
<meta http-equiv="Cache-Control" content="max-age=7200" />
<meta http-equiv="Expires" content="Mon, 01 Aug 2019 00:00:00 GMT" />
上述设置仅为举例,实际使用其一即可。这样写的话仅对该网页有效,对网页中的图片或其他请求无效,并不会做任何cache
。
这样客户端的请求就多了,尽管只是检查Last-modified
状态的东西,但是请求一多对浏览速度必定有影响。
Cache-Control
的参数包括:
- max-age=[单位:秒 seconds] — 设置缓存最大的有效时间. 类似于
Expires
, 但是这个参数定义的是时间大小(比如:60)而不是确定的时间点.单位是[秒 seconds]. - s-maxage=[单位:秒 seconds] — 类似于
max-age
, 但是它只用于公享缓存 (e.g., proxy) . - public — 响应会被缓存,并且在多用户间共享。正常情况, 如果要求 HTTP 认证,响应会自动设置为 private.
- private — 响应只能够作为私有的缓存(e.g., 在一个浏览器中),不能再用户间共享。
- no-cache — 响应不会被缓存,而是实时向服务器端请求资源。这一点很有用,这对保证HTTP 认证能够严格地禁止缓存以保证安全性很有用(这是指页面与public结合使用的情况下).既没有牺牲缓存的效率,又能保证安全。
- no-store — 在任何条件下,响应都不会被缓存,并且不会被写入到客户端的磁盘里,这也是基于安全考虑的某些敏感的响应才会使用这个。
- must-revalidate — 响应在特定条件下会被重用,以满足接下来的请求,但是它必须到服务器端去验证它是不是仍然是最新的。
- proxy-revalidate — 类似于
must-revalidate
,但不适用于代理缓存.
把css与js使用外联的方式进行使用
建议将css和js以外联方式引用以充分利用cache
,只有一个例外就是针对首页,首页可以采用内联来减少http请求数,加快页面显示速度,文中说首 页内联的原因是首页被人们访问的次数不会太多,而且样式一般比较特殊,不像其它页面一样有相同的页面模板(因为就有相同的样式部分),所以首页的样式可以 内联。并且应该在首页加载完成之后,再在后台动态加载后续页面的css和js,以提高后续页面的访问速度。
如果站点上的用户每个会话有多个页面查看,并且您的许多页面重复使用相同的脚本和样式表,则缓存的外部文件可能会带来更大的潜在好处。
对于现在的单页面的应用,更适合于动态加载的方案
缓存favicon.ico,并设置最好小于1k
favicon.ico是一个保留在服务器根目录中的图片,为了减轻拥有favicon.ico的带来的性能问题,请确保:
- 优化它的大小,最好不到1K。
- 设置Expires标头,使其缓存。
配置的ETag
ETag,全程为:Entity Tag
,意思是实体标签,它属于HTTP协议的一部分,也就是所有的Web服务器都应该支持这个特性。它的作用是用一个特殊的字符串来标识某个资源的“版本”,客户端(浏览器)请求的时候,比较ETag如果一致,则表示该资源并没有被修改过,客户端(浏览器)可以使用自己缓存的版本,避免重复下载。
它比last-modified date更具有弹性,例如某个文件在1秒内修改了10次,Etag可以综合Inode(文件的索引节点(inode)数),MTime(修改时间)和Size来精准的进行判断,避开UNIX记录MTime只能精确到秒的问题。 服务器集群使用,可取后两个参数。使用ETags减少Web应用带宽和负载。
响应标 | 优势 和特点 | 劣势 和可能的问题 |
---|---|---|
Expires | - HTTP 1.0就有,简单易用。 - 服务器通过这个Header告诉浏览器,某资源直到某个时间才会过期,所以在没有过期之前,浏览器就直接使用本地的缓存了。 | - 因为这是时间是由服务器发送的(UTC),但如果服务器时间和客户端事件存在不一致,可能会有些问题。 - 可能存在版本的问题,因为如果在到期之前修改过了,客户端是不会知道的。 - Cache-Control中的max-age可以实现类似的效果,但更加好,因为max-age是一个以秒为单位的时间数,而不是具体的时间,所以不存在上面提到的第一个问题。 |
Cache-Contro | - 服务器通过一个Header(Last-Modified)告诉浏览器,某资源最后修改的时间。 - 浏览器在请求的时候,包含一个Header(If-Modified-Since),然后服务器可以进行比较,如果在该时间后没有修改过,则返回304。 - 它比Expires多很多选项设置 | - Last-Modified 也是一个时间,但该时间只能精确到秒,如果在同一个秒中有多次修改(这个在现在的环境下应该确实是可能的),则可能会发生问题。 |
ETag | - 可以更加精确地判断资源是否被修改,因为它不是一个时间值,而是对时间经过处理的一个长整型数值(当然具体算法我们目前还不得而知)。 - 浏览器发起新请求时需要包含 If-None-Match。 | - 如果部署在服务器场环境中,配置不当的话,可能每个服务器会对相同的资源生成不一样的ETag,这样就增加了重复下载的可能性。 |
我们来看一个真实的例子:
其他的一些缓存使用:
- 使用本地缓存,缓存部分常用用户数据、公开数据
- 缓存机制设计:缓存+过期时间
总结一下:
- 添加Expires或Cache-Control
- Etag
- 缓存favicon.ico
- 外联js/css
12、缩短服务器响应时间
服务器响应时间受到访问流量,每个页面使用的资源,服务器使用的软件以及硬件本身的影响。要改善服务器响应时间,请查找性能瓶颈,如慢速数据库查询,慢速路由或缺少足够的内存并修复它们。最佳服务器响应时间低于200毫秒。
很多潜在因素都可能会延缓服务器响应,例如缓慢的应用逻辑、缓慢的数据库查询、缓慢的路由、框架、库、资源 CPU 不足或内存不足。您需要充分考虑所有这些因素,才能改善服务器的响应用时。 若想找出服务器响应用时过长的原因,首先要进行衡量。然后,准备好相关数据,并参阅有关如何解决该问题的相应指导。当解决问题后,您必须继续衡量服务器响应用时,并设法应对任何会在将来出现的性能瓶颈问题。
- 收集并检查现有性能和数据。若无可用内容,请使用自动化的网络应用监测解决方案(市面上有托管的开源版本,适用于大多数平台)进行评估,或添加自定义的方法。
- 找出并修复首要的性能瓶颈问题。如果z您使用的是热门网页框架或内容管理平台,请参阅与性能优化最佳做法相关的文档。
- 监测并提醒任何会在将来出现的性能衰退问题!
通常来说,服务器的响应时间由数据库的问题居多,还有错误的程序设计(比如:烂用for循环去查数据库)。
何解决该问题的相应指导。当解决问题后,您必须继续衡量服务器响应用时,并设法应对任何会在将来出现的性能瓶颈问题。
- 收集并检查现有性能和数据。若无可用内容,请使用自动化的网络应用监测解决方案(市面上有托管的开源版本,适用于大多数平台)进行评估,或添加自定义的方法。
- 找出并修复首要的性能瓶颈问题。如果z您使用的是热门网页框架或内容管理平台,请参阅与性能优化最佳做法相关的文档。
- 监测并提醒任何会在将来出现的性能衰退问题!
通常来说,服务器的响应时间由数据库的问题居多,还有错误的程序设计(比如:烂用for循环去查数据库)。