前端开发中,性能优化已经越来越被关注,本文我将对收集到的几种前端性能优化方式做一下归纳,希望对大家有所帮助。
一、资源压缩合并,减少http请求
合并图片(css sprites,精灵图,就是把多张小图片合并到一张大图里,本质就是减少请求)、CSS和JS文件合并压缩,图片较多的页面也可以使用 lazyLoad 等。
二、非核心代码异步加载
异步加载的方式:
- 动态脚本加载:使用document.createElement创建一个script标签,即document.createElement(‘script’),然后把这个标签加载到body上面去。
- 异步的方式加载 defer :
<script src="" defer></script>
- 异步的方式加载 async(HTML5新增特性):
<script src="" async></script>
defer和async的区别
- defer:在HTML解析完之后才会执行。如果是多个,则按照加载的顺序依次执行。
- async:在加载完之后立即执行。如果是多个,执行顺序和加载顺序无关。
三、利用浏览器缓存
发散阅读 浏览器的协商缓存与强缓存
缓存是所有性能优化的方式中最重要的一步,注意,缓存不是local storage 和session storage,浏览器缓存和存储不是一回事。
缓存的概念:
资源文件在本地存有副本,浏览器下次请求的时候,可能直接从本地磁盘里读取,而不会重新的请求。
缓存的分类:
1、强缓存:不用请求服务器,直接使用本地缓存,利用http响应头中的Exprires或Cache-Cantrol实现的。
注意:Exprires或Cache-Cantrol在response
header属性中,可以只启用一个,也可以同时启用,当Expires和Cache-Control同时存在时,Cache-Control的优先级高于Expires。
Expires和Cache-Control的区别:
1)Expires:服务器返回的绝对时间 是较老的强缓存管理 response header。浏览器再次请求这个资源时,先从缓存中寻找,找到这个资源后,拿出它的Expires跟当前的请求时间比较,如果请求时间在Expires的时间之前,就能命中缓存,否则就不行。如果缓存没有命中,浏览器直接从服务器请求资源时,Expires Header在重新请求的时候会被更新。
缺点: 由于Expires是服务器返回的一个绝对时间,存在的问题是:服务器的时间和客户端的时间可能不一致。在服务器时间与客户端时间相差较大时,缓存管理容易出现问题,比如随意修改客户端时间,就能影响缓存命中的结果。所以,在http1.1中,提出了一个新的response header,就是Cache-Control。
2)Cache-Control:服务器返回的相对时间 http1.1中新增的 response header。浏览器第一次请求资源之后,在接下来的相对时间之内,都可以利用本地缓存。超出这个时间之后,则不能命中缓存。重新请求时,Cache-Control会被更新。
2、协商缓存:浏览器发现本地有资源的副本,但是不太明确要不要使用,于是去问问服务器。 当浏览器对某个资源的请求没有命中强缓存(也就是说超出时间了),就会发一个请求到服务器,验证协商缓存是否命中。
协商缓存是利用的是两对Header:
第一对:Last-Modified、If-Modified-Since
第二对:ETag、If-None-Match
1)Last-Modified、If-Modified-Since。过程如下:
- 浏览器第一次请求一个资源,服务器在返回这个资源的同时,会加上Last-Modified这个 response header,这个header表示这该资源在服务器上的最后修改时间
- 浏览器再次请求这个资源时,会加上If-Modified-Since这个 request header,这个header的值就是上一次返回的Last-Modified的值
- 服务器收到第二次请求时,会比对浏览器传过来的If-Modified-Since和资源在服务器上的最后修改时间Last-Modified,判断资源是否有变化。如果没有变化则返回304 Not Modified,但不返回资源内容(此时,服务器不会返回 Last-Modified 这个 response header);如果有变化,就正常返回资源内容(继续重复整个流程)。
- 浏览器如果收到304的响应,就会从缓存中加载资源。
缺点:Last-Modified、If-Modified-Since一般来说都是非常可靠的,但有可能出现的问题是:服务器上的资源变化了,但是最后的修改时间却没有变化。这一对header就无法解决这种情况。于是,下面这一对header出场了。
2)ETag、If-None-Match。过程如下:
- 浏览器第一次请求一个资源,服务器在返回这个资源的同时,会加上ETag这个 response header,这个header是服务器根据当前请求的资源生成的唯一标识。这个唯一标识是一个字符串,只要资源有变化这个串就不同,跟最后修改时间无关,所以也就很好地补充了Last-Modified的不足。
- 浏览器再次请求这个资源时,会加上If-None-Match这个 request header,这个header的值就是上一次返回的ETag的值
- 服务器第二次请求时,会对比浏览器传过来的If-None-Match和服务器重新生成的一个新的ETag,判断资源是否有变化。如果没有变化则返回304 Not Modified,但不返回资源内容(此时,由于ETag重新生成过,response header中还会把这个ETag返回,即使这个ETag并无变化)。如果有变化,就正常返回资源内容(继续重复整个流程)。
- 浏览器如果收到304的响应,就会从缓存中加载资源。
四、使用CDN
原理就是把我们需要被分发的内容分发到世界各地的各个节点上,让世界各地的人都可以在距离最近的网络节点拿到想要拿到的内容,减少网络传输距离从而达到加速的目的。前端需要被加速的文件大致包括 js、css、图片、视频、和静态页面等文件。
五、DNS预解析
通过 DNS 预解析来告诉浏览器未来我们可能从某个特定的 URL 获取资源,当浏览器真正使用到该域中的某个资源时就可以尽快地完成 DNS 解析。
第一步:打开或关闭DNS预解析,你可以通过在服务器端发送 X-DNS-Prefetch-Control 报头。或是在文档中使用值为 http-equiv 的meta标签:
<meta http-equiv="x-dns-prefetch-control" content="on">
需要说明的是,在一些高级浏览器中,页面中所有的超链接(<a>
标签),默认打开了DNS预解析。但是,如果页面中采用的https协议,很多浏览器是默认关闭了超链接的DNS预解析。如果加了上面这行代码,则表明强制打开浏览器的预解析。(如果你能在面试中把这句话说出来,则一定是你出彩的地方)
第二步:对指定的域名进行DNS预解析
如果我们将来可能从 smyhvae.com 获取图片或音频资源,那么可以在文档顶部的 标签中加入以下内容:
<link rel="dns-prefetch" href="http://www.smyhvae.com/">
当我们从该 URL 请求一个资源时,就不再需要等待 DNS 解析的过程。该技术对使用第三方资源特别有用。
六、使用gzip压缩(部分浏览器支持)
gzip压缩就是将html\js\css文件压缩成gzip文件(.gz),在原有的压缩基础上,还可以再压缩70%左右,需要两步完成。
第一步:压缩gzip文件,webpack中可以使用compression-webpack-plugin插件完成,代码如下:
config.plugins.push(new CompressionPlugin({
test: /\.js$|\.html$|.\css/, //匹配文件名
threshold: 5240, //对超过5k的数据压缩
deleteOriginalAssets: false //不删除源文件
}));
第二步:在服务器端开启gzip支持。
tomcat:找到tomcat的server.xml文件,找到其中Connector节点然后进行配置修改,具体配置如下
<Connectorport="80"protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8" maxPostSize="0" useBodyEncodingForURI="true"compression="on" compressionMinSize="2048" noCompressionUserAgents="gozilla, traviata" compressableMimeType="text/html,text/xml,application/javascript,text/css,text/plain,image/jpeg,application/json"/>
参数说明:
- compression=“on” 打开压缩功能
- compressionMinSize=“2048” 启用压缩的输出内容大小,当被压缩对象的大小>=该值时才会被压缩,这里面默认为2KB
- noCompressionUserAgents=“gozilla, traviata” 对于以下的浏览器,不启用压缩
- compressableMimeType=“text/html,text/xml,text/javascript,text/css,text/plain” 压缩类型
注意:tomcat7以后,js文件的mimetype类型变为了application/javascript,而在tomcat7以下则为text/javascript;具体的tomcat7定义的类型可以在:conf/web.xml文件中找到。
可以在web.xml下搜索,如我搜索javascript会找到如下代码
<mime-mapping>
<extension>js</extension>
<mime-type>application/javascript</mime-type>
</mime-mapping>
nginx:gzip使用环境:http,server,location,if(x),一般把它定义在nginx.conf的http{……}之间
- gzip on
on为启用,off为关闭 - gzip_min_length 1k
设置允许压缩的页面最小字节数,页面字节数从header头中的Content-Length中进行获取。默认值是0,不管页面多大都压缩。建议设置成大于1k的字节数,小于1k可能会越压越大。 - gzip_buffers 4 16k
获取多少内存用于缓存压缩结果,‘4 16k’表示以16k*4为单位获得 - gzip_comp_level 5
gzip压缩比(1~9),越小压缩效果越差,但是越大处理越慢,所以一般取中间值; - gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php
对特定的MIME类型生效,其中’text/html’被系统强制启用 - gzip_http_version 1.1
识别http协议的版本,早起浏览器可能不支持gzip自解压,用户会看到乱码 - gzip_vary on
启用应答头"Vary: Accept-Encoding" - gzip_proxied off
nginx做为反向代理时启用,off(关闭所有代理结果的数据的压缩),expired(启用压缩,如果header头中包括"Expires"头信息),no-cache(启用压缩,header头中包含"Cache-Control:no-cache"),no-store(启用压缩,header头中包含"Cache-Control:no-store"),private(启用压缩,header头中包含"Cache-Control:private"),no_last_modefied(启用压缩,header头中不包含"Last-Modified"),no_etag(启用压缩,如果header头中不包含"Etag"头信息),auth(启用压缩,如果header头中包含"Authorization"头信息) - gzip_disable msie6
(IE5.5和IE6 SP1使用msie6参数来禁止gzip压缩 )指定哪些不需要gzip压缩的浏览器(将和User-Agents进行匹配),依赖于PCRE库
以上代码可以插入到 http {…}整个服务器的配置里,也可以插入到虚拟主机的 server {…}或者下面的location模块内