单页面系统的一些性能优化
Posted 我叫悠哉
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单页面系统的一些性能优化相关的知识,希望对你有一定的参考价值。
1:初次进入系统的新用户
首次进入该系统的用户,没有任何的文件缓存。进入系统后加载index.html 会将所有的文件(图片、js、css)下载下来 耗时在3.3s左右。之后进入系统 只会额外加载一些页面的图片,不会再加载js、css文件。
2:已经进过该系统的老用户,且系统未更新之前
用户本地的文件都已经被缓存(图片、css、js),在系统更新之前,所有的文件都没有更改,用户再进入系统时,速度会在500ms之内,可以从下图看到,所有的文件加载速度都是为0的。
3:已经进入过该系统的老用户,且系统已经更新
分析:
用户访问系统时会下载index.html文件,此时,如果index里引入的文件(图片、js、css)的文件名没有改变,客户端(浏览器)会仍然从缓存中取数据。但每次迭代更新都会更改css、js,每次的改动都会导致再次打包时该文件的文件名发生改变,此时,用户就会重新下载这个被改动的文件。如果被改动的文件的大小比较大,那么该用户仍然会在再次进入该系统时花费较多的时间从服务器下载该文件,但介于有很多资源并不会改变,比如引入的一些插件(包含js、css、图片)从始至终并不会去改变的东西,那么用户首次进入该系统时就已经缓存,一直不需要从服务器下载。
4:关于打包
不管如何打包,系统的所有文件都会被浏览器下载,能做的有以下几点。
1:缩小所有的文件(js、图片、css)
Js/css:能复用的尽量复用,不写冗余的代码。(注释,空格会在打包时自动去除进行压缩,所以注释可以多写)
html:我们将所有的html都打包进了js文件,这样可以在改变html时更改js文件名,这样在系统更新之后,老用户可以重新下载html,一次性下载,之后不会在从服务器下载html
针对于以上的机制,用户在登录系统期间,即使我们将服务器上的所有的代码删除掉,用户仍然可以在系统之内正常运行,因为用户已经下载了index页面及这个index页面所引入的各个文件。
5:现在的打包情况
grunt的配置方法:
先将ngtemplete任务删除,然后filerev(为文件提供md5值)会根据usemin配置的文件指向,将所指向的文件加上md5值,这时候所有的html里引用的文件名会根据打包之后的名字而进行替换。
测试方法:
1:进入系统后,先将所有的html都点击一次,这样,就保证了所有的html文件都被浏览器所下载
此时,用户不关闭窗口,将服务器上的前端文件全部删除,用户仍然可以正常操作该系统。如果用户之前有一个页面从来没有使用过,即没有请求后台拿到数据后缓存在浏览器内这一步操作。将会得到一个404的页面返回。如下图,我在录单页面(正常使用)去访问一个从来没使用过的页面(发车确认)就会发生404请求。
原因:html、js、css都已经被客户端下载,客户端可以正常使用并且访问服务器。
2:关闭浏览器窗口,重新进入系统。
这时用户是无法访问到我们的网页的。(这里牵涉到客户端的一些关闭、刷新等操作对缓存的影响)
- 浏览器地址栏中写入URL,回车
浏览器发现缓存中有这个文件了,不用继续请求了,直接去缓存拿。(最快) - F5
F5就是告诉浏览器,别偷懒,好歹去服务器看看这个文件是否有过期了。于是浏览器就发送一个请求带上If-Modify-since。 - Ctrl+F5
告诉浏览器,你先把你缓存中的这个文件给我删了,然后再去服务器请求个完整的资源文件下来。于是客户端就完成了强行更新的操作.
此时会产生的缓存问题:
问题1:直接写入url,回车,这个时候会发现,用户是从缓存中取出的index.html,这样一来,及时我们升级了系统,老用户也看不到新的系统,这个时候只能选择刷新。
2:f5刷新:
刷新时,大家可以看到任然是缓存,但是状态是304,说明这时是请求了服务器。优化历程中,我们在线上上的一个版本是在退出时默认刷新了页面,这样就解决了问题1。
后来我们选择了在退出的时候将地址重新定向,在index.html后面加上了一个随机数,这样也就相当于重新打开了一个页面
3:ctrl+f5
这种就是完全抛弃了缓存,完全从服务器上下载资源,不符合我们的预期。
现在的打包选择了:
1: html从后台获取,这样就会减少新用户首次加载网页时的下载量的大小。
2:对于echart.min.js与viewer.min.js采用了异步加载方式,这样可以减少js的加载对dom的渲染的阻塞,可以使用户更快的见到我们的页面,减少空白页面的持续时间。
3:退出时 刷新页面,重新加载了index,index里引入的html因为不是以js做的缓存,所以会跟服务器通信。可以实时监控到html的文件变化
4:对于js,css文件。因为服务端设置的是缓存时间为0,所以每次进入系统都会跟服务器做通信,index页面的重新加载,会监控到js文件的变化。之所以在chrome上看到的200状态,其实也是做了通信的,只是chrome将其与304状态混淆了,其实还是有通信。.
5:前端index文件加上了nocache meta 保证每次先跟服务端通信再加载。
6:浏览器缓存状态的说明
每个状态的详细说明如下:
1、Last-Modified
在浏览器第一次请求某一个URL时,服务器端的返回状态会是200,内容是你请求的资源,同时有一个Last-Modified的属性标记(HttpReponse Header)此文件在服务期端最后被修改的时间,格式类似这样:
Last-Modified:Tue, 24 Feb 2009 08:01:04 GMT
客户端第二次请求此URL时,根据HTTP协议的规定,浏览器会向服务器传送If-Modified-Since报头(HttpRequest Header),询问该时间之后文件是否有被修改过:
If-Modified-Since:Tue, 24 Feb 2009 08:01:04 GMT
如果服务器端的资源没有变化,则自动返回HTTP304(NotChanged.)状态码,内容为空,这样就节省了传输数据量。当服务器端代码发生改变或者重启服务器时,则重新发出资源,返回和第一次请求时类似。从而保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。
注:如果If-Modified-Since的时间比服务器当前时间(当前的请求时间request_time)还晚,会认为是个非法请求
2、Etag工作原理
HTTP协议规格说明定义ETag为“被请求变量的实体标记”(参见14.19)。简单点即服务器响应时给请求URL标记,并在HTTP响应头中将其传送到客户端,类似服务器端返回的格式:
Etag:“5d8c72a5edda8d6a:3239″
客户端的查询更新格式是这样的:
If-None-Match:“5d8c72a5edda8d6a:3239″
如果ETag没改变,则返回状态304。
即:在客户端发出请求后,HttpReponse Header中包含Etag:“5d8c72a5edda8d6a:3239″
标识,等于告诉Client端,你拿到的这个的资源有表示ID:5d8c72a5edda8d6a:3239。当下次需要发Request索要同一个URI的时候,浏览器同时发出一个If-None-Match报头(Http RequestHeader)此时包头中信息包含上次访问得到的Etag:“5d8c72a5edda8d6a:3239″标识。
If-None-Match:“5d8c72a5edda8d6a:3239“
,这样,Client端等于Cache了两份,服务器端就会比对2者的etag。如果If-None-Match为False,不返回200,返回304(Not Modified) Response。
3、Expires
给出的日期/时间后,被响应认为是过时。如Expires:Thu, 02 Apr 2009 05:14:08 GMT
需和Last-Modified结合使用。用于控制请求文件的有效时间,当请求数据在有效期内时客户端浏览器从缓存请求数据而不是服务器端.当缓存中数据失效或过期,才决定从服务器更新数据。
4、Last-Modified和Expires
Last-Modified标识能够节省一点带宽,但是还是逃不掉发一个HTTP请求出去,而且要和Expires一起用。而Expires标识却使得浏览器干脆连HTTP请求都不用发,比如当用户F5或者点击Refresh按钮的时候就算对于有Expires的URI,一样也会发一个HTTP请求出去,所以,Last-Modified还是要用的,而且要和Expires一起用。
5、Etag和Expires
如果服务器端同时设置了Etag和Expires时,Etag原理同样,即与Last-Modified/Etag对应的HttpRequestHeader:If-Modified-Since和If-None-Match。我们可以看到这两个Header的值和WebServer发出的Last-Modified,Etag值完全一样;在完全匹配If-Modified-Since和If-None-Match即检查完修改时间和Etag之后,服务器才能返回304.
6、Last-Modified和Etag
分布式系统里多台机器间文件的last-modified必须保持一致,以免负载均衡到不同机器导致比对失败
分布式系统尽量关闭掉Etag(每台机器生成的etag都会不一样)
Last-Modified和ETags请求的http报头一起使用,服务器首先产生Last-Modified/Etag标记,服务器可在稍后使用它来判断页面是否已经被修改,来决定文件是否继续缓存
过程如下:
1.客户端请求一个页面(A)。
2.服务器返回页面A,并在给A加上一个Last-Modified/ETag。
3.客户端展现该页面,并将页面连同Last-Modified/ETag一起缓存。
4.客户再次请求页面A,并将上次请求时服务器返回的Last-Modified/ETag一起传递给服务器。
5.服务器检查该Last-Modified或ETag,并判断出该页面自上次客户端请求之后还未被修改,直接返回响应304和一个空的响应体。
注:
1、Last-Modified和Etag头都是由WebServer发出的HttpReponse Header,WebServer应该同时支持这两种头。
2、WebServer发送完Last-Modified/Etag头给客户端后,客户端会缓存这些头;
3、客户端再次发起相同页面的请求时,将分别发送与Last-Modified/Etag对应的HttpRequestHeader:If-Modified-Since和If-None-Match。我们可以看到这两个Header的值和WebServer发出的Last-Modified,Etag值完全一样;
4、通过上述值到服务器端检查,判断文件是否继续缓存;
7、关于 Cache-Control: max-age=秒 和 Expires
Expires = 时间,HTTP 1.0 版本,缓存的载止时间,允许客户端在这个时间之前不去检查(发请求)
max-age = 秒,HTTP 1.1版本,资源在本地缓存多少秒。
如果max-age和Expires同时存在,则被Cache-Control的max-age覆盖。
Expires 的一个缺点就是,返回的到期时间是服务器端的时间,这样存在一个问题,如果客户端的时间与服务器的时间相差很大,那么误差就很大,所以在HTTP 1.1版开始,使用Cache-Control: max-age=秒替代。
Expires =max-age + “每次下载时的当前的request时间”
所以一旦重新下载的页面后,expires就重新计算一次,但last-modified不会变化
以上是关于单页面系统的一些性能优化的主要内容,如果未能解决你的问题,请参考以下文章