项目的线上版本我们一般都会结合构建工具(webpack
)插件和服务端配置(nginx
)来实现 http 传输 的 gzip 压缩,目的就是把服务端响应的文件的体积尽量减小,优化返回速度。那这件事具体是怎么实现的呢?
http 传输中 gzip 压缩的原理
客户端(浏览器)在请求静态资源(js、css等)的时候,在请求头 Request Header 里带上 accept-encoding
字段来表明接受哪些压缩方法, 如accept-encoding: gzip, deflate
;服务端在接收到请求时如果发现有这个配置,则发送gzip压缩版本的文件,并在响应头 Response Headers 配置一个 content-encoding
字段,用于说明服务端数据的压缩方法(可选值是gzip、compress、deflate
),否则(没有accept-encoding
)就发送源文件;客户端再根据返回响应头里 content-encoding
对应的格式去做相应的解码/解压缩;没有content-encoding
项则不进行解压缩。
accept-encoding
和content-encoding
字段都非常语义化,就是传输的文件以怎样的格式编码/解码,所以当响应头有content-encoding: gzip
出现,就能说明我们服务端的gzip压缩成功开启了。如下:
同时我们还能看到压缩后的文件 size,记得先在Chrome浏览器的 Network 下勾选 ☑️Use large request rows
谁来压缩文件?
(1) 服务端响应请求时压缩
如果我们在服务器用Nginx代理部署项目,就直接让 nginx 来处理压缩,它有专门为此构建的内容,可以更好地利用缓存并减少开销。我们要做的只是在服务端的 nginx.conf
做好配置,其他就不需要我们操心了。
配置参数可参考:Nginx的gzip配置文档
# 开启gzip压缩
gzip on;
gzip_buffers 4 16k; # 置用于压缩响应的缓冲区的数量和大小
gzip_comp_level 9; # 对响应压缩的级别,可选范围:1到9,数字越大压缩得越好,但也越占用CPU时间
gzip_http_version 1.1; # 默认 1.1,请求压缩响应所需的最小HTTP版本
gzip_min_length 1k; # 设置被gzip的响应的最小长度,小于该值的文件不会被压缩
# 追加启用gzip压缩的MIME类型,默认已有text/html
gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php application/javascript application/json;
gzip_disable "MSIE [1-6]\.";
gzip_vary on; # 默认off,如果指令gzip、gzip_static或gunzip是active的,启用插入" Vary: Accept-Encoding "响应报头字段
具体配置位置展示
(2) 项目打包构建生产版本时压缩
既然服务端都可以做压缩,为什么在 webpack 打包应用时还要多此一举呢?我们可以看上面 nginx 配置中 gzip_comp_level
这个配置项,数字越大压缩效果越好,但是会耗费更多的CPU和时间,我们压缩文件主要是为了减少传输时间,如果每次请求静态资源服务端都要压缩很久才会返回信息,不仅本末倒置吗?况且服务器开销也会增大很多。既然现在的 spa 应用文件都是打包生成的,我们在打包的时候就直接生成高压缩等级的文件,作为静态资源放在服务器上,接收到请求后直接把这些压缩版文件返回回去不就好了?
webpack 的 compression-webpack-plugin 就是专门做这件事情的:
tip:我bulid的时候报了Cannot read property 'tapPromise' of undefined
的错,其实就是版本和vue-cli的某些包不兼容,把 compression-webpack-plugin 的版本降低到6.1.1
就可以了。
先安装npm install compression-webpack-plugin -D
,然后到vue.config.js配置:
const CompressionPlugin = require("compression-webpack-plugin");
configureWebpack: config => {
config.name = name
const plugins = []
if (IS_PROD) { // 生产环境
plugins.push(
// 为静态资源准备压缩版本,在服务器也要开启相应配置
new CompressionWebpackPlugin({
test: /\.(js|css|json|ico|svg)$/,// 匹配文件格式
algorithm: 'gzip',
threshold: 10240, // 对超过10k的数据压缩
minRatio: 0.8, // 压缩比
// filename: "[path][base].gz", // 压缩后的文件名,默认值是 [path][base].gz
filename(pathData) {
// `pathData` 参数包含很多可以获取到文件路径相关数据的属性 - `path`/`name`/`ext`/等等
// 如果路径中包含svg,则放到svg/目录下
// 只是演示,一般都用字符串默认值就好
if (/\.svg$/.test(pathData.ext)) {
return 'static/svg/[base].gz'
}
return '[path][base].gz'
},
deleteOriginalAssets: false, // 不删除源文件,true 则只保留压缩后的文件
})
)
} else {
// 为开发环境修改配置
}
config.plugins = [...config.plugins, ...plugins]
},
其中 filename
参数就是定义文件编码压缩后的路径和文件名,格式除了字符串也可以是Function
(基本没啥必要)。
默认值是"[path][base].gz"
,一般保持默认值就好。
gz是文件后缀,那[path]
和 [base]
是啥呢
比如我们有这么个静态资源:static/images/image.png?foo=bar#hash
(static
是我打包目录下自定义的静态资源目录),然后若我们给插件 filename 的值里配置如下参数,完成压缩后输出的文件名中:
-
[path]
会被替换为源静态资源的目录, 包括末尾的/
(static/images/
) -
[file]
为源静态资源的路径 (static/images/image.png
) -
[name]
为源文件的文件名 (image
) -
[ext]
为源文件(包含.
)的扩展名 (.png
) -
[base]
会被替换为 ([name]
+[ext]
) 的内容 (image.png
) -
[query]
为源文件的查询参数,包括?
(?foo=bar
) -
[fragment]
为源文件的 fragment (在URL的概念里叫做hash
)(#hash
)
参考文章:探索HTTP传输中gzip压缩
网友评论