欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

浅析前端性能优化总结

程序员文章站 2024-01-26 12:40:28
...

性能优化总结:减少请求次数、减小资源大小、提高响应和加载速度、优化资源加载时机、优化加载方式

性能分类

对前端工程性能的优化,我觉得可以分为两类:

  • 站在用户视角的主观的可感知的性能。
  • 站在开发者视角的可客观度量的性能。

总之一句话,你的页面可以做的不快,但是你可以让你的用户觉得你很快。

客观性能是指,从用户输入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-cacheno-store,默认先走强缓存路线。根据 cache-controlexpires 优先级低)判断缓存是否过期,若没有过期则此时返回 200(from cache)

    2)若本地缓存已经过期再走协商缓存路线,根据之前的 last-modified 值去与服务器比对,若这个时间之后没有改过则去读取本地缓存,返回 304(not modified)

    3)否则返回新的资源,状态码 200(ok),并更新返回响应的 last-modified 值。

    2、按刷新按钮、F5 刷新、网页右键“重新加载”:

    这种情况下,实际是浏览器将 cache-controlmax-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>
  • 我们访问一个站点时,简单来说,都会经过以下的步骤:

    1. DNS解析
    2. TCP握手
    3. 如果为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

浅析前端性能优化总结