个人理解优化最开始需要做的是:将现有资源有效利用到极致,然后在做更多提升。开发人员多了解一些优化技术,不仅可以怒斥前端写的代码辣鸡,甚至可以和架构师谈笑风生。😀
在进行 web 性能优化之前,我们先重温一下,访问一个页面地址到浏览器渲染完页面,都有哪些过程:
资源请求/获取流程图:
资源响应/页面渲染流程图:
这两张图无需额外解释了,所以 web 性能优化主要是针对这里的过程进行优化,上车:
DNS 优化
在与服务主机建立连接之前,需要先解析域名;那么,解析越快就越好。
- 限制不同域名的数量。数量多了自然会消耗更多的解析时间,https 时代这个成本更高。
- 保证低限度的解析延迟。了解你的 DNS 服务基础设施的结构,然后从你的最终用户分布的所有地域定期监控解析时间。
- 在主体页面 HTML 或响应中利用 DNS 预取指令。例如:
<link rel="dns-prefetch" href="//oss.aliyun.com>
优化TCP连接
开启新连接是一个耗时的过程,https 有加签名过程,更是耗时。
- 利用 preconnect 指令,连接在使用之前就已经建立好了,这样处理流程的关键路径上就不必考虑连接时间了。
<link rel="preconnect" href="//oss.aliyun.com" crossorigin>
- 借助 CDN,在距离请求用户很近的边缘端点上,请求就可以获得响应, 所以可以终止连接,大幅减少建立新连接的通信延迟。静态资源上 cdn 现在成为常态 + 廉价的优化手段了。
避免重定向
重定向通常触发与额外域名建立连接。手机网络环境下,重定向会增加很大延迟,毕竟iPhone 高通基带用户越来越多了,照顾信号差的用户。所以可以:
- 利用 CDN 代替客户端在云端实现重定向。
- nginx 层面用 rewrite 命令搞定。
利用缓存
本地缓存:没有什么比直接从本地缓存获取资源来得更快,因为它根本就不需要建立网络连接。最快的请求是根本不发起请求。另外,从本地获取资源时,ISP 或 CDN 提供商不会收取流量费。设置生存时间(TTL)告诉浏览器应该缓存某个资源多久。找到给定资源的最佳 TTL 值并没有完美的科学方法。设置客户端缓存 TTL,可以通过 HTTP 首部指定 cache control 以及键 max-age(以秒为单位),或者 expires 首部。
网络缓存:对于可以共享,能够接收一定旧数据的资料,可以在网络边缘缓存。主要是不需要实时性很高的资源文件可以网络缓存。
条件缓存:如果缓存 TTL 过期,客户端会向服务器发起请求。在多数情况下,收到的响应其实和缓存的版本是一样的,重新下载已经在缓存里的内容也是一种浪费。当资源不经常变化时,使用条件请求可以显著节省带宽和性能;可以通过以下手段使用条件缓存:
- 在请求中包含 HTTP 首部 Last-Modified-Since。仅当最新内容在首部中指定的日期之后被更新过,服务器才返回完整内容;否则只返回 304 响应码,并在响应首部中附带上新的时间戳 Date 字段。
- 在请求体中包含实体校验码,或者叫 ETag;它唯一标识所请求的资源。ETag 由服务器提供,内嵌于资源的响应首部中。服务器会比较当前 ETag 与请求首部中收到的 ETag,如果一致,就只返回 304 响应码;否则返回完整内容。依然是静态资源文件需要缓存。(可以参考 Nginx 内容缓存机制)
压缩和代码极简化
所有的文本内容(HTML、JS、CSS、SVG、XML、JSON、字体等),可以从压缩和极简 化中受益。这两种方法组合起来,可以显著减少资源大小。更少字节数对应着更少的请求-响应,也就意味着更短的请求时间。它通过可无损还原的算法减少资源大小。在发送资源之前,如果服务器进行压缩处理,可以节省 90% 的大小。
极简化是指从文本资源中剥离所有非核心内容的过程。极简化是放弃代码可读性与可维护性来减少字节数,个人不推荐使用。
避免阻塞CSS/JS
尽管浏览器的预处理器很智能,会尽早请求整个页面所需要的 CSS,但是把 CSS 资源请求放在页面靠前的部分仍然是种最佳实践,具体位置是在页面代码的 head 标签里,而且要在任何 JS 或图片被请求和处理之前。
浏览器默认的阻塞行为导致了不必要的延迟,甚至会造成单点故障。为了减轻 JS 阻塞带来的潜在影响,推荐使用以下策略:
- 定期校验这些资源的使用情况。随着时间的变迁,Web 页面可能会持续下载一些不再 需要的 JS;这时候,最快速有效的解决办法就是去掉它。
- 如果 JS 执行顺序无关紧要,并且必须在 onload 事件触发之前运行,那么可以设置 async 属性,例如:
<script async src="/js/myfile.js">
- 如果 JS 执行顺序很重要,并且你也能承受脚本在 DOM 加载完之后运行,那么请使用 defer 属性,例如:
<script defer src="/js/myjs.js">
- 对不会影响到页面初次展示的 JS 脚本,必须在 onload 事件触发之后请求它。
- 如果你不想延迟主页面的 onload 事件,可以考虑通过 iframe 获取 JS,因为它的处理独立于主页面。但是,通过 iframe 下载的 JS 访问不了主页面上的元素。
优化图片
图片在现在很重要,而且高质量的图片体积很大,移动设备访问的时候,会有明显的加载慢问题,同时移动端用户耐心相对较低,所以对图片的优化刻不容缓:
- 去掉图片原始的信息,尤其是照片类,会包含位置,拍照设备等信息,去掉原始信息能减少很多体积。
- 提前裁剪图片,然后在页面的 js 判断需要加载哪种尺寸的图片,这种操作能直接选择小尺寸图片,对于页面的微信分享展示也是不错的提升。
- 考虑第三方的图片压缩算法,例如 Google 的 webp 格式图片,压缩比例高,不会影响图片质量。提前处理,然后加载压缩后的图片
参考书籍为 《HTTP2基础教程》,O’Reilly Media 出版社,自己调整了一些内容,希望不会收到律师函。