前言
互联网有一项闻名的8秒准绳。用户在访问Web网页时,若是时间超过8秒就会感应不不耐烦,若是加载时间太长,他们就会放弃访问。大部分用户希望网页能在2秒之内就完成加载。事实上,加载时间每多1秒,你就会流失7%的用户。8秒并不是切确的8秒钟,只是向网站开发者剖清楚明了加载时间的重要性。那我们应该如何优化页面性能,加快页面加载速度呢?本文只关注一些要点,以下是小编总结的几种常见的方法:
资源压缩与合并
主要包含以下几个方面:
- html压缩
- css压缩
- js压缩和紊乱
- 文件合并
资源压缩可以从文件中去掉多余的字符, 比如回车和空格.
1. html压缩
html代码压缩就是压缩这些在文本文件中有意义,可是在HTML中不需要的字符,比如: 空格,制表符,换行符等,还有一些其他意义的字符,如HTML注释也可以被压缩。
2. css代码压缩
css代码压缩简单来说就是无效代码删除和css语义合并
3.js的压缩和紊乱
如何进行js代码的压缩和紊乱呢?
- 使用在线网站进行压缩
- 使用html-minifier工具
- 使用uglifyjs2对js进行压缩
其实css压缩与js的压缩和紊乱比html压缩收益要大得多,同时css代码和js代码比html代码多得多。所以对大公司来说,html压缩无关紧要,但css压缩与js的压缩非常有必要!
4. 文件合并
将多个js/css小文件合并为一个文件, 减少网络请求数.
非核心必要代码异步加载
1. 异步加载
实现异步加载有三种可行办法:
- async
- defer
- 动态创建script标签
async
- async属性是HTML5新增属性, 目前在Chrome, Firefox, IE9+上支持.
- async的设置,会使得script脚本异步的加载并在允许的情况下执行
- async的执行,并不会按着script在页面中的顺序来执行,而是谁先加载完谁执行。
defer
- 兼容所有浏览器
- 如果script标签设置了该属性,则浏览器会异步的下载该文件并且不会影响到后续DOM的渲染;
- 如果有多个设置了defer的script标签存在,则会按照顺序执行所有的script;
- defer脚本会在文档渲染完毕后,DOMContentLoaded事件调用前执行。
动态创建script标签
在没有defer和async属性前, 异步加载是通过动态创建script标签, 在window.onload事件中触发动态创建script到dom中实现的.
示例代码:
function addScriptTag(src){
var script = document.createElement('script');
script.setAttribute("type","text/javascript");
script.src = src;
document.body.appendChild(script);
}
window.onload = function{
addScriptTag("js/index.js");
}
2. defer&async异步加载的区别
- defer是在HTML解析完之后才会实行,若是是多个,按照加载的依次实行
- async是在加载完之后立即实行,若是是多个,实行按次和加载按次无关
浏览器缓存
对于web应用来说, 缓存是提升页面性能同时减少服务器压力的利器.
浏览器缓存
1. 强缓存
不会向服务器发送请求, 直接从缓存中读取资源, 在Chrome控制台的network选项中可以看到该请求的状态码为200, 但size标识为from dist cache
或者from memory cache
.
2. response header
Expires
response header里的过期时间, 浏览器再次加载该资源时, 如果在有效时间内, 则使用强缓存, 它的值是一个绝对时间(GMT时间字符串), 如: Expires:Thu,21 Jan 2018 23:39:02 GMT
Cache-Control
(1) 请求Request:
- no-cache ---- 不要读取缓存中的文件,要求向WEB服务器重新请求
- no-store ---- 请求和响应都禁止被缓存
- max-age: ---- 表示当访问此网页后的max-age秒内再次访问不会去服务器请求,其功能与Expires类似,只是Expires是根据某个特定日期值做比较。一但缓存者自身的时间不准确.则结果可能就是错误的,而max-age,显然无此问题.。Max-age的优先级也是高于Expires的。
- max-stale ---- 允许读取过期时间必须小于max-stale 值的缓存对象。
- min-fresh ---- 接受其max-age生命期大于其当前时间 跟 min-fresh 值之和的缓存对象
- only-if-cached ---- 告知缓存者,我希望内容来自缓存,我并不关心被缓存响应,是否是新鲜的.
- no-transform ---- 告知代理,不要更改媒体类型,比如jpg,被你改成png.
(2) 响应Response:
- public ---- 数据内容皆被储存起来,就连有密码保护的网页也储存,安全性很低
- private ---- 数据内容只能被储存到私有的cache,仅对某个用户有效,不能共享
- no-cache ---- 可以缓存,但是只有在跟WEB服务器验证了其有效后,才能返回给客户端
- no-store ---- 请求和响应都禁止被缓存
- max-age: ----- 本响应包含的对象的过期时间
- Must-revalidate ---- 如果缓存过期了,会再次和原来的服务器确定是否为最新数据,而不是和中间的proxy
- max-stale ---- 允许读取过期时间必须小于max-stale 值的缓存对象。
- proxy-revalidate ---- 与Must-revalidate类似,区别在于:proxy-revalidate要排除掉用户代理的缓存的。即其规则并不应用于用户代理的本地缓存上。
- s-maxage ---- 与max-age的唯一区别是,s-maxage仅仅应用于共享缓存.而不应用于用户代理的本地缓存等针对单用户的缓存. 另外,s-maxage的优先级要高于max-age.
- no-transform ---- 告知代理,不要更改媒体类型,比如jpg,被你改成png.
Last-Modified 和 If-Modified-Since
都是用于记录页面最后修改时间的 HTTP 头信息,注意:
- Last-Modified 是由服务器往客户端发送的 HTTP
- If-Modified-Since是由客户端往服务器发送的头
再次请求本地存在的 cache 页面时,客户端会通过 If-Modified-Since 头将先前服务器端发过来的 Last-Modified 最后修改时间戳发送回去,这是为了让服务器端进行验证,通过这个时间戳判断客户端的页面是否是最新的,如果不是最新的,则返回新的内容,如果是最新的,则 返回 304 告诉客户端其本地 cache 的页面是最新的,于是客户端就可以直接从本地加载页面了,这样在网络上传输的数据就会大大减少,同时也减轻了服务器的负担。而且在一些ajax应用中,要求获取的数据永远是最新的,而不是读取位于缓存中的数据,做这样的设置是非常有必要的。
但Last-Modified存在一些缺点:
- 有些服务器无法获取文件最新的修改时间
- 文件的修改时间变了, 但内容可能没有变化
既然按文件修改时间无法保证缓存是否合适, 那么是否可以考虑按文件内容来决定文件是否缓存呢?—-ETag和If-None-Match
ETag 和 If-None-Match
-
ETag是上一次加载资源时, 服务器返回给浏览器客户端的response header, 是对该资源的一种唯一标识, 只要资源有变化, Etag就会重新生成, 浏览器每次加载资源向服务器发送请求时, 都会将文件上一次的Etag值放到request header的If-None-Match中, 服务器与客户端传入的If-None-Match进行比对, 看是否一致, 就能很好的断定资源是否有更新过. 若匹配, 则返回HTTP 304状态码, 浏览器则会直接使用本地缓存, 否则, 返回HTTP 200状态码, 返回最新的资源 .
Etag & Last-Modified 两者比较:
- 从准确度来看, Etags要优于 Last-Modified, Last-Modified设置时间的最小单位是秒, 若某个文件在1秒内修改了两次或更多, Last-Modified无法精确的保证文件是否是最新的, 但Etag可以. 若是负载均衡服务器, 每个服务器生成的 Last-Modified值也可能不一致.
- 从性能来看, Etag要逊于 Last-Modified, 因为 Last-Modified只需要设置时间即可, 而Etag则必须要服务器通过比对算法来比较hash值.
- 从优先级来看, 服务器优先考虑校验Etag
Cache-Control
浏览器缓存机制流程图
CDN
CDN服务提供商会有全国各个省份部署节点, 将网站静态资源部署到CDN后, 用户在访问页面时, CDN静态资源会从就近的CDN节点上加载资源. 当请求至达CDN节点后, 节点会判断资源是缓存是否有效, 若有效, 直接返回给用户, 若无效, 会从CDN服务器加载最新的资源返回给用户同时将资源保存一份到该CDN节点上, 以便后续的访问用户使用. 因此, 只在该地区有一个用户先加载了资源, 在CDN中建立了缓存, 该地区的其他用户都能受益.
DNS预解析
DNS 作为互联网的基础协议,其解析的速度似乎容易被网站优化人员忽视。现在大多数新浏览器已经针对DNS解析进行了优化,典型的一次DNS解析耗费20-120 毫秒,减少DNS解析时间和次数是个很好的优化方式。DNS Prefetching是具有此属性的域名不需要用户点击链接就在后台解析,而域名解析和内容载入是串行的网络操作,所以这个方式能减少用户的等待时间,提升用户体验。
预解析的实现过程:
-
- 用meta信息来告知浏览器, 当前页面要做DNS预解析:
-
- 在页面header中使用link标签来强制对DNS预解析:
注:dns-prefetch需慎用,多页面重复DNS预解析会增加重复DNS查询次数。
PS:DNS预解析主要是用于网站前端页面优化,在SEO中的作用湛蓝还未作验证,但作为增强用户体验的一部分rel="dns-prefetch"或许值得大家慢慢发现。
浏览器对网站第一次的域名DNS解析查找流程依次为:
浏览器缓存-系统缓存-路由器缓存-ISP DNS缓存-递归搜索
Chrome内置了DNS Prefetching技术, Firefox 3.5 也引入了这一特性,由于Chrome和Firefox 3.5本身对DNS预解析做了相应优化设置,所以设置DNS预解析的不良影响之一就是可能会降低Google Chrome浏览器及火狐Firefox 3.5浏览器的用户体验。
本文摘至网络