gzip压缩分为服务器压缩和前端文件压缩。应该二者结合,综合使用。
一、问题描述
有一个VUE项目,开发环境下,使用npm运行,一切正常,发布部署到nginx上一片空白。用谷歌浏览器进入开发者工具,提示什么“Uncaught SyntaxError: Unexpected token ‘<’ ”
为什么会报这个错误呢?这与nginx的设置有关。
location / {
root E:/订餐谁拿饭抓阄评估规划执行监控审计管理系统/code/web/dist;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
- 1
- 2
- 3
- 4
- 5
最后这一句,当出错的时候会强制跳转到 index.html 页面,而js文件不能识别html,因此报Uncaught SyntaxError: Unexpected token < 错误。
更确切地说,是浏览器向服务器请求js文件,服务器报错了,依照nginx的设定,转跳到了index.html,即nginx向客户端返回了index.html的内容。而浏览器接收到这部分"javascript"内容后,无法解释,于是就报了“Uncaught SyntaxError: Unexpected token <”的错。
为什么向服务器请求js文件会报错呢?原因是,在我们的发布包里,没有这些js文件,只有js文件的压缩包:
nginx应该是可以识别这些压缩包,能处理gzip,所以没有报404错误(如果将发布包部署到IIS,会报404错误),但不知道什么原因,nginx没有正确返回js内容,而是触发了异常。
二、vue的文件压缩处理
之所以我这个vue项目的发布文件,有*.gz文件,是因为配置文件vue.config.js中指定使用了压缩插件:
switch (process.env.NODE_ENV) {
case "development":
...
break;
case "production"://vue3默认情况下,npm run build时,process.env.NODE_ENV==="production"
...
WEBPACK_PLUGINS.push(
new CompressionWebpackPlugin({
filename: "[path].gz[query]",
algorithm: "gzip",
test: new RegExp("\\.(" + productionGzipExtensions.join("|") + ")$"),
threshold: 10240,
minRatio: 0.8,
deleteOriginalAssets: true,//删除压缩后的原文件
})
);
break;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
三、nginx的gzip设置
事实上,对于服务器来说,有2种应用gzip的途径。一是我们发布文件的时候,不做任何处理,由nginx在处理客户端请求时,将内容压缩返回;二就是我们发布压缩文件,nginx直接读取并返回给客户端。
两种方案,nginx的设置有所不同。
1、nginx压缩
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
server {
listen 8001;
server_name localhost;
gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
#gzip_http_version 1.0;
gzip_comp_level 8;
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
gzip_proxied any;
gzip_vary off;
gzip_disable "MSIE [1-6]\.";
location / {
root E:/订餐谁拿饭抓阄评估规划执行监控审计管理系统/code/web/dist;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
使用浏览器观察,可以看到
2、发布文件压缩
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
server {
listen 8001;
server_name localhost;
#会优先查找静态gzip资源
gzip_static on;
location / {
root E:/订餐谁拿饭抓阄评估规划执行监控审计管理系统/code/web/dist;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
我的情况,就属于第二种方案,然而却出现了问题。出现问题的原因,是应该保留压缩前的原文件,而不是删掉。即部署时,被压缩文件既有压缩包,又有原文件。nginx会优先查找静态gzip资源。要保留原文件的原因,是nginx用来索引和对照的?
使用浏览器观察,
注意方案1的ETag存在"W/"开头,而方案2的ETag则没有。‘W/’(区分大小写)表示使用弱验证器 。弱 etags 很容易生成,但在比较时用处不大。强验证器是比较的理想选择,但很难有效地生成。ETag弱相同,表示两个资源可能在语义上是等效的,但不是逐字节相同的。这意味着当使用字节范围请求时,弱 ETag 会阻止缓存,但强 ETag 意味着范围请求仍然可以被缓存。
3、两种压缩方案的对比
对比第一种和第二种方式,发现请求ETag有区别,第二种应该比较好。避免前端没有做压缩处理,服务端最好兼容两种都配置,存在静态gzip则用静态资源,不存在则服务端处理。
四、解决方案
综上,解决方案很简单,就是我们在发布的时候,既要压缩,又要保留压缩前的文件,即将deleteOriginalAssets的值设为false。
vue.config.js
WEBPACK_PLUGINS.push(
new CompressionWebpackPlugin({
filename: "[path].gz[query]",
algorithm: "gzip",
test: new RegExp("\\.(" + productionGzipExtensions.join("|") + ")$"),
threshold: 10240,
minRatio: 0.8,
deleteOriginalAssets: false,//删除压缩后的原文件
})
);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
nginx采用方案2或者结合起来使用均可。
五、小结
这样子发布出来的包,比之前更大了。但是,可以带来性能的提升,值得去做,这点存储空间不算什么。
为什么系统会提供一个删掉压缩包原文件的选项呢?事实已经证明,这种模式在nginx上不被支持。我猜测,这可能是用于运行在node服务器上的。
参考文章:
Vue 项目性能优化之gzip