前端性能优化之加载

@zhuanyongxigua 2018-08-07 08:36:26发表于 zhuanyongxigua/blog

首先需要注意的问题

性能优化的最终目标不是让网站在任何特定设备上都能运行很快,而是使用户满意,让用户成为您的性能工作的中心。

实践时的思考过程:

  1. 需要发送什么资源?

    发送有用的资源,无用的或意义不大的资源要想办法处理。再具体一点,比如Bootstrap这样的UI库是否必须?类似jQuery这样的工具库是否必要?是否一定要做成SPA(SPA会大量使用JavaScript,JavaScript是最昂贵的资源,它需要经过下载、解析、编译和执行的过程)?

  2. 如何发送这些资源?

    比如尝试HTTP/2,HTTP/2发送大量的请求的开销很低,这样资源打包的意义就下降了;使用可以加快递送资源的提示(rel=preload)等。

  3. 需要发送多少数据?

    具体就是资源本身的优化了,如压缩打包。用NetworkInformation API帮助自己处理网络条件不好的情况下的优化。

几种常用的优化方法

资源压缩合并,减少HTTP请求

文本内容的处理

压缩网页可以用gzip,浏览器会自动解压缩并渲染。使用率不高的库应该考虑移除,自己写代替的方法。

图片的处理

图片可压缩的空间比较大。

移除不必要的图片

对于每一张图片,首先都要想一想,是否真的需要这张图片?如果需要,是否可以延迟加载?不要让无意义的图片影响有意义的内容。如果图片不是为了提供更多内容而仅仅是为了好看,就要考虑优化了。

选用合适的图片格式

png,支持透明和半透明,可以无损,缺点是文件太大;

gif,只有256色,支持透明,不支持半透明,文件也很大,可以尝试使用webm;

jpg,比较最小,不支持透明。

webp格式,体积又小,又可以半透明,不好的地方就是有些浏览器可能不支持,需要做判断。

去除图片元数据

元数据是拍照设备留下的信息,如拍照日期、时间戳、拍照应用的设置、地理位置等等。可以试试VerExif。这个优化可能看起来节省的不多,可如果图片非常多的话,非服务器带宽的节省是非常可观的。

调整图片大小

使用与显示区域大小合适的图片。如果图片过大,不但下载时间长,浏览器还需要额外的处理。所有的地方都是用同一张图片,会很简单,节省代码,后端不需要处理图片,前端也不需要做判断。可是,这样做会浪费带宽,带宽是更昂贵的资源。可以通过修剪图片或降低图片质量的方式实现。

不要在一个小的区域内加载一张大图,显示的是多大,图片就用多大的。实际的操作就是往后台传参数,传一个大小,如果后台有合适的图片,就把合适的图片返回来,如果没有,就先把原图返回来,然后后台再默默的转格式,这样再次加载的时候就会有合适的图片了。

图片压缩

减小图片大小,而不太影响图片的尺寸和质量。使用较多的提供压缩服务的网站是TinyPgn

HTTP请求

上面的思路都是减小文件大小,下面思路是如何减少频率。

与上面的区别是,上面的方式是要压缩,这里的方式是合并不同的文件或图片。

需要注意的是,如果使用的是HTTP2的协议,是资源变小的“压缩”类的思路依然可以使用,可把资源合并的价值就没有那么大了。因为HTTP2的速度很快,如果你把动态资源跟静态资源合并到了一块,还要避免缓存导致需要重新加载整个资源,这样就更得不偿失了。

script标签尽量放在下面,挨着body的闭合标签。目的是不让script的加载阻塞内容的渲染。小段的代码可以尝试直接写在script标签里面,而不是给script标签加上一个链接,这样可以较少HTTP请求。

非核心代码异步加载

需要思考的内容有:异步加载的方式有哪些?这些方式之间的区别?

异步加载的方式

动态脚本加载

动态脚本加载,就是用document去创建script节点。后面两个是script标签的属性。

defer

defer会在html解析完成之后才执行。

async

默认是async,谁先回来就先加载谁。

defer与async的相同点是都是在其他的script执行完了之后,区别是defer还要等html解析完成之后才会执行,如果是多个,按照加载顺序依次执行;而async是在加载完成之后立即执行,如果是多个,执行顺序和加载顺序无关。

此外link标签的rel属性还有preload和prefetch这样的值,preload是在当前页的生命周期的早期开始获取,不易阻塞页面的渲染,可以预加载css和js等,详细看mdn。prefetch是把资源的优先级降低,当前页可能不用,是下一页会用到的资源,先提前加载缓存了。兼容可能都会有点问题。还有preconnect这样的属性值,先链接一下资源,但是并不加载,以提高性能。

利用浏览器缓存

缓存的使用对网页加载速度的提高有非常明显的效果。需要根据资源的类型考虑缓存时间,比如公司的logo,改变的可能很小,就可以把缓存的时间设置的很长;可如果是动态的资源,可能会经常变动,就不能把缓存的时间设置的太长。

缓存的分类

浏览器在获取资源的时候,先会判断这个资源的header信息中的强缓存字段,如果命中,则使用本地缓存,不会与服务器发生通讯;如果没有命中,则再根据协商缓存判断。

强缓存

强缓存就是根据给定的时间判断是否使用缓存,状态码是200(from cache)

Expires,是绝对时间,例如Expires: Thu, 21 Jan 2017 23:39:02 GMT。

Cache-Control,是相对时间,例如Cache-Control:max-age=3600,优先级高于Expires。由于客户端的时间可能不准确,这就可能导致使用绝对时间判断是否使用缓存会不准确。

Cache-Control常用的可选项有:

  • no-cache,会有缓存,不过每次都要询问服务器;
  • no-store,不会有缓存,与no-cache对应;
  • public,谁都可以缓存;
  • private,默认值,只有自己能缓存,别人不给缓存,一般就是浏览器,中间的代理不能缓存(如cdn);
  • max-age,缓存最大时间,用这个数与资源的第一次请求时间相加,在于现在的时间比较,一般建议最大不超过一年。

协商缓存

协商缓存会询问服务器,由服务器判断是否使用缓存,状态码是304(not modified)或200。

有两组字段:

Last-Modified和If-Modified-Since,例如Last-Modified: Wed, 26 Jan 2017 00:35:11 GMT,只能精确到秒,Last-Modified是服务器返回的,下次请求的时候会用If-Modified-Since字段再把这个时间发给服务器做判断。

Etag和If-None-Match,优先级更高,是一个哈希值,Etag由服务器返回,下次请求的时候再用If-None-Match把这个哈希值发给服务器,用于比较。如果是没有变化的话,状态码就是304,重定向到本地。Etag的使用会比Last-Modified多,一秒之内修改两次的情况用Last-Modified无法解决。

缓存的安全

安全的根本是https,缓存的安全需要了解SRICRC

其他的缓存还有app cache、PWA和SW。app cache使用的较少,后面的两个,兼容性还有一些问题。

使用CDN

CDN提高网页速度的效果也很明显,一般用于静态资源(图片等),应用较多。一般不带cookie,这样可以减少请求头的大小。CDN可以减少主域的压力,降低成本。此外,各个浏览器对于同一个域下的资源,对同时请求的数量是有限制的,如果用CDN,就可以突破这种限制。使用CDN也可以提高安全性,如果遇到攻击,CDN可以挡住部分的攻击。

预解析DNS

<meta http-equiv="x-dns-prefetch-control" content="on">
<link rel="dns-prefetch" href="//host_name_to_prefetch.com">

html所有的a标签默认是打开预解析的,如果是https,很多是关闭的,用这个标签,强制打开预解析。

参考资料: