download:小程序音乐项目开发实战coderwhy
Web前端性能优化自查清单
前言
一份简洁、纯粹的Web前端性能优化清单。每个优化点都包含有概念、实操和参考资料。面试、实战两相宜。
这是一个大工程。在正式开始之前,先统一下语言,澄清每一部分的目的和要求,防止跑偏。
概念:把官话翻译成能看懂、能记住的人话,原则上易读性 > 专业性
实操:自己操作一遍,不做云玩家;记录核心实现,方便CV
参考资料:信息来源选用一手资料,以便保证信息的完整性、准确性和时效性。除非看一手的理解不了……
一、网络层面
- DNS预解析
概念
DNS-prefetch 是一种 DNS 预解析技术。它会在请求跨域资源之前,预先解析并进行DNS缓存,以减少真正请求时DNS解析导致的请求延迟。对于打开包含有许多第三方连接的网站,效果明显。
实操
添加ref属性为“dns-prefetch”的link标签。一般放在在html的head中。
复制代码
href的值就是要预解析的域名,对应后面要加载的资源或用户有可能打开链接的域名。
备注
同理,也有“ TCP/IP预连接”,叫preconnect。参考资料中有完整的描述。 - 应用浏览器缓存
概念
浏览器缓存是浏览器存放在本地磁盘或者内存中的请求结果的备份。当有相同请求进来时,直接响应本地备份,而无需每次都从原始服务器获取。这样不仅提升了客户端的响应效率,同时还能缓解服务器的访问压力。
其间,约定何时、如何使用缓存的规则,被称为缓存策略。分为强缓存和协商缓存。
整个缓存执行的过程大致如下:
①. 请求发起,浏览器判断本地缓存,如果有且未到期,则命中强缓存。浏览器响应本地备份,状态码为200。控制台Network中size那一项显示disk cache;
②. 如果没有缓存或者缓存已过期,则请求原始服务器询问文件是否有变化。服务器根据请求头中的相关字段,判断目标文件新鲜度;
③. 如果目标文件没变更,则命中协商缓存,服务器设置新的过期时间,浏览器响应本地备份,状态码为304;
④. 如果目标文件有变化,则服务器响应新文件,状态码为200。浏览器更新本地备份。
上述过程有几个关键点
如何判断缓存是否过期?
浏览器读取缓存的请求结果中响应头的Expires 和Cache-Control,与当前时间进行比较。
其中,Expires是HTTP 1.0的字段,值是一个是绝对时间。
Expires: Tue, 18 Jan 2022 09:53:23 GMT
复制代码
比较绝对时间,有一个弊端,它依赖计算机时钟被正确设置。
为了解决这个问题,HTTP1.1 新增了Cache-Control字段,它的值是一个是相对时间。
Cache-Control: max-age=60 //单位是秒
复制代码
如何判断文件是否变化?
首先可以通过比较 最后修改时间。
// 缓存结果的 响应头
Last-Modified: Mon, 10 Jan 2022 09:06:14 GMT
// 新请求的 请求头
If-Modified-Since: Mon, 10 Jan 2022 09:06:14 GMT
复制代码
浏览器取出缓存结果中Last-Modified的值,通过If-Modified-Since上送到服务端。与服务器中目标文件的最后修改时间做比较。
再者可以通过比较 Etag。
Etag实体标签是附加到文档上的任意标签(引用字符串)。它们可能包含了文档的序列号或版本名,或者是文档内容的校验和及其他指纹信息。当发布者对文档进行修改时,会修改文档的实体标签来说明这是个新的版本。
从响应头的ETag取值,通过请求头的If-None-Match上送,与服务器目标文件的Etag标签比对。
// 缓存的 响应头
ETag: "61dbf706-142"
// 上送的 请求头
If-None-Match: "61dbf706-142"
复制代码
和上面一样,新增的字段也是为了解决前一种方案的某些缺陷:
有些文档可能会被周期性地重写(比如,从一个后台进程中写入),但实际包含的数据常常是一样的。尽管内容没有变化,但修改日期会发生变化。
有些文档可能被修改了,但所做修改并不重要,不需要让世界范围内的缓存都重装数据(比如对拼写或注释的修改)。
有些服务器无法准确地判定其页面的最后修改日期。
有些服务器提供的文档会在亚秒间隙发生变化(比如,实时监视器),对这些服务器来说,以一秒为粒度的修改日期可能就不够用了。
如果两个版本的字段同时存在,怎么办?
出于浏览器兼容方面的考虑 ,一般两组字段会被同时使用。他们没有优先级一说,取并集。
同时出现时,只有当两个条件都满足,才会命中相应缓存。
实操
缓存是web服务器和浏览器的核心能力,主流的web服务框架 nginx、koa-static等都内置有上述缓存策略的实现。开箱即用,无需额外编程或配置。
以Nginx举例。强缓存的配置字段是expires,它接受一个数字,单位是秒。
server {
listen 8080;
location / {
root /Users/zhp/demo/cache-koa/static;
index index.html;
# 注意try_files会导致缓存配置不生效
# try_files $uri $uri/ /index.html;
expires 60;
}
}
复制代码
实际工作中确实配置一下就好了,但这体现不出什么知识点。为了加深印象,我这用koa简陋的模拟了一下,算是对上面那些知识点的验证。
下面是一个极简的静态资源服务,不带缓存的。
app.use(async (ctx) => {
// 1.根据访问路径读取指定文件
const content = fs.readFileSync(./static${ctx.path}
, "utf-8");
// 2.设置响应
ctx.body = content;
});
复制代码
这种情况,无论访问多少次都是不进缓存的。
现在,在响应头加上强缓存所需的Exprise和Cache-Control字段
app.use(async (ctx) => {
// 1.根据访问路径读取指定文件
const content = fs.readFileSync(./static${ctx.path}
, "utf-8");
// 2.设置缓存
ctx.response.set("Cache-Control", "max-age=60");
ctx.response.set('Exprise', new Date(new Date().getTime()+60*1000));
// 3.设置响应
ctx.body = content;
});
复制代码
查看Network,响应头会多出下面两个字段,且间隔60秒内的请求会走缓存,符合预期。
Expires: Tue, 18 Jan 2022 10:05:09 GMT
Cache-Control: max-age=60
复制代码
备注
抱着引用一手权威资料的想法,扒了《HTTP权威指南》,但读感着实差强人意。新手建议《图解HTTP》起手,要友好很多。
参考资料
《HTTP权威指南》
HTTP 缓存机制
Nginx中文文档
- 静态资源CDN
概念
CDN的全称是Content Delivery Network,即内容分发网络。CDN是构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。
核心功效总结起来就两点:
①. 通过负载均衡技术 ,为用户的请求选择最佳的服务节点;
②. 通过内容缓存服务,提高用户访问响应速度。
实操
普通玩家:选择一个CDN服务商,看它提供的使用文档。通过配置域名和源站,代理到自己的静态资源服务器。
高级玩家:自建CDN服务器,balabal……
参考资料
阿里云CDN快速入门
- 开启Gzip
概念
gzip是GNUzip的缩写,最早用于UNIX系统的文件压缩。HTTP协议上的gzip编码是一种用来改进web应用程序性能的技术,web服务器和客户端(浏览器)必须共同支持gzip。gzip压缩比率在3到10倍左右,可以大大节省服务器的网络带宽。
实操
实际操作过程中分为动态压缩和静态压缩。
动态压缩。指当收到请求后,服务器实时压缩然后输出数据流。服务器存放的是css/js文件。
Nginx的httpGzip模块,支持该功能。主要配置如下:
开启或者关闭gzip模块
gzip on;
设置允许压缩的页面最小字节数。建议设置成大于1k的字节数,小于1k可能会越压越大。
gzip_min_length 1024;
匹配MIME类型进行压缩,(无论是否指定)"text/html"类型总是会被压缩的。
gzip_types text/plain application/x-javascript text/css text/html application/xml;
复制代码
静态压缩。服务器一开始存放的就是已经压缩好的文件,当接受请求后直接响应压缩资源,而不是收到请求后才压缩。
使用Webpack + Nginx的实现:
①. 安装并应用compression-webpack-plugin压缩插件
// ## 安装 ##
// 注意高版本会报错 Cannot read property 'tapPromise' of undefined
npm i --save-dev [email protected]
// ## webpack配置 ##
// vue.config.js
const CompressionPlugin = require("compression-webpack-plugin");
module.exports = {
configureWebpack:{
plugins: [
new CompressionPlugin()
]
}
}
复制代码
②. 执行构建npm run build
打包完成后,在dist目录下会多出.gz结尾的压缩文件
③. Nginx配置开启gzip_static
http{
gzip_static on;
server {
listen 8082;
location / {
root /Users/zhp/demo/demo-externals/dist;
}
}
}
复制代码
结果验证
在Response Header中看到有Content-Encoding: gzip,说明服务器配置生效;
在Network的Size列看数据比服务器上源文件要小,说明浏览器支持,Gzip生效。
备注
gzip_static的优先级高于gzip。当gzip和gzip_static都开启时,nginx会优先匹配.gz文件,然后才走动态压缩。
参考资料:
你真的了解 gzip 吗?
www.npmjs.com/package/com…
- 使用高版本的HTTP协议
概念
从1.0到1.1再到如今的2.0,HTTP协议在持续迭代中,变的更快更强。
其间的变更内容多且硬核,这里出于解释高版本优势的目的,简单的列举一二,HTTP/1.1的持久连接和管道化技术、2.0的多路复用和首部压缩。
持久连接
HTTP 协议的初始版本中,每进行一次 HTTP 通信就要断开一次 TCP连接。为了减少了TCP 连接的重复建立和断开所造成的额外开销。 HTTP/1.1 和一部分的 HTTP/1.0 想出了 持久连接(HTTP Persistent Connections,也称为 HTTP keep-alive 或HTTP connection reuse)的方法。持久连接的特点是,只要任意一端 没有明确提出断开连接,则保持 TCP 连接状态。