浅析前端性能优化总结
性能优化总结:减少请求次数、减小资源大小、提高响应和加载速度、优化资源加载时机、优化加载方式
性能分类
对前端工程性能的优化,我觉得可以分为两类:
- 站在用户视角的主观的可感知的性能。
- 站在开发者视角的可客观度量的性能。
总之一句话,你的页面可以做的不快,但是你可以让你的用户觉得你很快。
客观性能是指,从用户输入url开始,到下载、解析和执行所有资源以及最终绘制的整个过程的时间度量。
减少请求次数
- webpack里url-loader设置limit,图片超过limit大小时,解析成base64位,减少图片请求;
减小资源大小
-
开启gzip 压缩或者compression-webpack-plugin,
gzip
压缩效率非常高,通常可以达到 70% 的压缩率 -
terser-webpack-plugin插件js压缩,并且去除 console.log()和注释;
-
去除SourceMap
-
将公共资源放到CDN上,配合webpack的externals和cdn
提高打包速度
- Webpack.dllPlugin,autoDllPlugin,hardSourcePlugin抽离公共资源
- happyPack,thread-loader多进程打包
提高响应和加载速度
-
网络资源优化 Service Worker
-
ServiceWorker
是运行在浏览器后台进程里的一段 JS,它可以做许多事情,比如拦截客户端的请求、向客户端发送消息、向服务器发起请求等等,其中最重要的作用之一就是离线资源缓存。 -
ServiceWorker
拥有对缓存流程丰富灵活的控制能力,当页面请求到ServiceWorker
时,ServiceWorker
同时请求缓存和网络,把缓存的内容直接给用户,而后覆盖缓存
-
-
注意:需要HTTPS才可以使用 ServiceWorker
-
http缓存
- 缓存策略分析:
1、标签进入、输入url回车进入:
1)如果没有设置
no-cache
和no-store
,默认先走强缓存路线。根据cache-control
(expires
优先级低)判断缓存是否过期,若没有过期则此时返回200(from cache)
。2)若本地缓存已经过期再走协商缓存路线,根据之前的
last-modified
值去与服务器比对,若这个时间之后没有改过则去读取本地缓存,返回304(not modified)
。3)否则返回新的资源,状态码
200(ok)
,并更新返回响应的last-modified
值。2、按刷新按钮、F5 刷新、网页右键“重新加载”:
这种情况下,实际是浏览器将
cache-control
的max-age
直接设置成了 0,让缓存立即过期,直接走协商缓存路线3、ctrl + F5 强制刷新:
强制刷新的情况下,浏览器会强行设置请求头cache-control:
no-cache
,强制获取最新的资源,就连 请求头里的if-modified-since
等其他缓存协议字段都会被吃掉。- 什么是缓存
浏览器缓存(Brower Caching)是浏览器对之前请求过的文件进行缓存,以便下一次访问时重复使用,节省带宽,提高访问速度,降低服务器压力。
HTTP 缓存一般分为两类:强缓存(200,也称本地缓存) 和 协商缓存(也称304缓存)。
普通刷新会启用 协商缓存,忽略 强缓存。在地址栏或收藏夹输入网址、通过链接引用资源等情况下,浏览器才会启用 强缓存。
-
浏览器是如何判断是否使用缓存的
第一次请求:
第二次请求相同网页:
-
强缓存(200):浏览器不会向服务器发送任何请求,直接从本地缓存中读取文件并返回Status Code: 200 OK
本地缓存是最快速的一种缓存方式,只要资源还在缓存有效期内,浏览器就会直接在本地读取,不会请求服务端。
200 form memory cache : 不访问服务器,一般已经加载过该资源且缓存在了内存当中,直接从内存中读取缓存。浏览器关闭后,数据将不存在(资源被释放掉了),再次打开相同的页面时,不会出现from memory cache。
200 from disk cache:不访问服务器,已经在之前的某个时间加载过该资源,直接从硬盘中读取缓存,关闭浏览器后,数据依然存在,此资源不会随着该页面的关闭而释放掉下次打开仍然会是from disk cache。
- 协商缓存(304) : 向服务器发送请求
协商缓存,顾名思义是经过浏览器与服务器之间协商过之后,在决定是否读取本地缓存,如果服务器通知浏览器可以读取本地缓存,会返回304状态码,并且协商过程很简单,只会发送头信息,不会发送响应体。
向服务器发送请求,服务器会根据这个请求的request header的一些参数来判断是否命中协商缓存,如果命中,则返回304状态码并带上新的response header通知浏览器从缓存中读取资源;
- 缓存位置
缓存位置一般分为:Memory Cache(内存缓存)和 Disk Cache(硬盘缓存)
内存缓存:读取快、持续时间短、容量小
硬盘缓存:读取慢、持续时间长、容量大
- 缓存优先级
Service Worker -> Memory Cache -> Disk Cache -> 网络请求
-
强缓存和协商缓存的header参数
强缓存:
1.Expires:过期时间,如果设置了时间,则浏览器会在设置的时间内直接读取缓存,不再请求;Expires因为是对时间设定的,且时间是格林Greenwich Mean Time (GMT),而不是本地时间,所以对时间要求较高;
2.Cache-Control:当值设为max-age=300时,则代表在这个请求正确返回时间(浏览器也会记录下来)的5分钟内再次加载资源,就会命中强缓存。
3.cache-control:除了该字段外,还有下面几个比较常用的设置值:
1) max-age:用来设置资源(representations)可以被缓存多长时间,单位为秒;
2) s-maxage:和max-age是一样的,不过它只针对代理服务器缓存而言;
3)public:指示响应可被任何缓存区缓存;
4)private:只能针对个人用户,而不能被代理服务器缓存;
5)no-cache:强制客户端直接向服务器发送请求,也就是说每次请求都必须向服务器发送。服务器接收到请求,然后判断资源是否变更,是则返回新内容,否则返回304,未变更。这个很容易让人产生误解,使人误以为是响应不被缓存。实际上Cache-Control: no-cache是会被缓存的,只不过每次在向客户端(浏览器)提供响应数据时,缓存都要向服务器评估缓存响应的有效性。
6)no-store:禁止一切缓存(这个才是响应不被缓存的意思)。
cache-control是http1.1的头字段,expires是http1.0的头字段,如果expires和cache-control同时存在,cache-control会覆盖expires,建议两个都写。
协商缓存:
Last-Modifed/If-Modified-Since和Etag/If-None-Match是分别成对出现的,呈一一对应关系.
-
Etag/If-None-Match:
Etag:
Etag是属于HTTP 1.1属性,它是由服务器(Apache或者其他工具)生成返回给前端,用来帮助服务器控制Web端的缓存验证。 Apache中,ETag的值,默认是对文件的索引节(INode),大小(Size)和最后修改时间(MTime)进行Hash后得到的。
If-None-Match:
当资源过期时,浏览器发现响应头里有Etag,则再次像服务器请求时带上请求头if-none-match(值是Etag的值)。服务器收到请求进行比对,决定返回200或304
-
-
-
-
Last-Modifed/If-Modified-Since:
Last-Modified:
浏览器向服务器发送资源最后的修改时间
If-Modified-Since:
当资源过期时(浏览器判断Cache-Control标识的max-age过期),发现响应头具有Last-Modified声明,则再次向服务器请求时带上头if-modified-since,表示请求时间。服务器收到请求后发现有if-modified-since则与被请求资源的最后修改时间进行对比(Last-Modified),若最后修改时间较新(大),说明资源又被改过,则返回最新资源,HTTP 200 OK;若最后修改时间较旧(小),说明资源无新修改,响应HTTP 304 走缓存。
-
Last-Modifed/If-Modified-Since的时间精度是秒,而Etag可以更精确。
-
Etag优先级是高于Last-Modifed的,所以服务器会优先验证Etag
-
Last-Modifed/If-Modified-Since是http1.0的头字段
-
-
-
-
HTTP2
HTTP2 四个新特性:
-
多路复用,无需多个TCP连接,因为其允许在单一的HTTP2连接上发起多重请求,因此可以不用依赖建立多个TCP连接。
-
二进制分帧,将所有要传输的消息(响应体和请求体)采用二进制编码,并且会将信息分割为更小的消息块。
-
头部压缩,用HPACK技术压缩头部,减小报文大小
-
服务端推送,服务端可以在客户端发起请求前发送数据,换句话说,服务端可以对客户端的一个请求发送多个响应,并且资源可以正常缓存。
注意:使用 http2 的前提是必须是 https。
-
资源预加载
link标签的使用:
总结:对当前页面需要的资源,使用 preload 进行预加载,对其它页面需要的资源进行 prefetch 预加载。
-
preload:当前页面加载的过程中,在浏览器开始主体渲染之前加载。
<!-- 对sty1e.css和 index.js进行pre1oad预加载 -->
<link rel="preload" href="style.css" as="style">
<link rel="preload" href="index.js" as="script">
dns-prefetch:使得转化工作提前进行了,缩短了请求资源的耗时。
<link rel="dns-prefetch" href="//example.com">
-
什么时候使用呢?当我们页面中使用了其他域名的资源时,比如我们的静态资源都放在cdn上,那么我们可以对cdn的域名进行预解析。
-
Preconnect:预链接
<link rel="preconnect" href="//example.com">
<link rel="preconnect" href="//cdn.example.com" crossorigin>
-
我们访问一个站点时,简单来说,都会经过以下的步骤:
- DNS解析
- TCP握手
- 如果为Https站点,会进行TLS握手
使用preconnect后,浏览器会针对特定的域名,提前初始化链接(执行上述三个步骤),节省了我们访问第三方资源的耗时。需要注意的是,我们一定要确保preconnect的站点是网页必需的,否则会浪费浏览器、网络资源。
-
prefetch:预拉取,页面加载完成后,利用空闲时间提前加载其他页面的资源。
<!--对资源进行 prefetch预加载-->
<link rel="prefetch" href="next.css">
<link rel="prefetch" href="next.js">
<link rel="prefetch" href="//example.com/next-page.html" as="document" crossorigin="use-credentials">
<link rel="prefetch" href="/library.js" as="script">
-
使用了prefetch,资源仅仅被提前下载,下载后不会有任何操作,比如解析资源。
-
Prerender:预渲染
-
<link rel="prerender" href="//example.com/next-page.html">
-
prerender比prefetch更进一步。不仅仅会下载对应的资源,还会对资源进行解析。
解析过程中,如果需要其他的资源,可能会直接下载这些资源。这样,用户在从当前页面跳转到目标页面时,浏览器可以更快的响应。
-
指的注意: Resource Hints只是一些『提示』,浏览器可以采用我们的提示,但是具体怎么实现还是由浏览器自己来决定的。比如,如果当前CPU压力大,网络阻塞时,你使用了Prefetch,那么浏览器可能仅仅会只对dns进行预解析,并不会下载资源。
javascript资源加载方式
-
正常的脚本会阻塞下面代码的执行,DOM渲染,一般建议放在body下面。
<script src="a.js" ></script>
1.停止解析 document.
2.请求 a.js
3.执行 a.js 中的脚本
4.继续解析 document
defer
异步加载 js 文件,并且不会阻塞页面的渲染。
<script src="d.js" defer></script>
<script src="e.js" defer></script>
1.不阻止解析 document, 并行下载 d.js, e.js;
2.即使下载完 d.js, e.js 仍继续解析 document;
3.按照页面中出现的顺序,在其他同步脚本执行后,DOMContentLoaded 事件前依次执行 d.js, e.js;
async
<script src="b.js" async></script>
<script src="c.js" async></script>
1.不阻止解析 document, 并行下载 b.js, c.js
2.当脚本下载完后立即执行。(两者执行顺序不确定,执行阶段不确定,可能在 DOMContentLoaded 事件前或者后 )
用户感知性能优化
- 图片懒加载 imgLazyLoad
- 骨架屏
如果大家想学习前端方面的技术,我把我多年的经验分享给大家,还有一些学习资料,分享Q群:1046097531