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;
}

最后这一句,当出错的时候会强制跳转到 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;
}

三、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;
		}
	}
}

使用浏览器观察,可以看到
在这里插入图片描述

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;
		}
	}
}

我的情况,就属于第二种方案,然而却出现了问题。出现问题的原因,是应该保留压缩前的原文件,而不是删掉。即部署时,被压缩文件既有压缩包,又有原文件。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,//删除压缩后的原文件
      })
    );

nginx采用方案2或者结合起来使用均可。

五、小结

这样子发布出来的包,比之前更大了。但是,可以带来性能的提升,值得去做,这点存储空间不算什么。

为什么系统会提供一个删掉压缩包原文件的选项呢?事实已经证明,这种模式在nginx上不被支持。我猜测,这可能是用于运行在node服务器上的。

参考文章:
Vue 项目性能优化之gzip

Logo

前往低代码交流专区

更多推荐