gzip在web中的应用

使用webpack打包前端文件时,在一些资料中提到可以使用gzip压缩技术达到优化打包文件大小的效果。 简单配置后,发现压缩效果确实很好,可对前端文件缩小至少一半左右! 惊讶之余同时也有很多疑问,这篇文章主要是想记录下笔者对gzip在web中应用的探索过程。

带上几个小问题

比如gzip压缩技术,如何用到已有的web项目中呢? 在其它地方看到nginx也可以做gzip,那么webpack和nginx的gzip有什么关系吗?
带上这些问题,开始查阅资料和实践总结,一点点了解它吧!

gzip和文件压缩

gzip是若干种文件压缩程序的简称,“GNU计划” 的实现,使用c语言实现。
gzip的基础是DEFLATE,DEFLATE是LZ77与哈夫曼编码的一个组合体。初始版本于1992年10月31日,25年前发布。
可见,gzip原本是在Unix-like操作系统上用来做数据压缩的一种工具。

gzip和http协议

HTTP压缩是一种内置到网页服务器和网页客户端中以改进传输速度和带宽利用率的方式,最常见的压缩方案包括gzip和Deflate。具体压缩算法还有其它类型,这里不做展开。可见gzip只是http压缩方案中的其中一种,其完整流程需要客户端和服务端同时支持。
那么服务端和客户端分别如何支持gzip的呢?

客户端

  • 支持http压缩的客户端在发送请求的header里配置 Accept-Encoding字段,用于说明自己能够理解的内容编码方式。在接收到返回的数据后去检查服务端响应头,然后根据对应的格式去做相应的解码,目前主流的浏览器,Chrome,firefox,IE等都支持该协议。
  • 不支持http压缩的客户端在发送请求的header里配置不会添加Accept-Encoding字段。

服务端
服务端接收到客户端请求头Accept-Encoding字段后,端会选择其中一种压缩方案进行压缩,使用并在响应报文首部 Content-Encoding 中表示消息主体进行了何种方式的内容编码转换,告知客户端应该怎样解码才能获取在 Content-Type 中标示的媒体类型内容。常见的服务器如Apache,Nginx,IIS都支持gzip。

HTTP协议gzip压缩的过程

gzip在web中的应用

  1. 客户端发送Http request 给Web服务器, 兼容压缩方案的浏览器会在http请求头中带上有Accept-Encoding: gzip。(告诉服务器, 客户端支持gzip压缩)
  2. 服务器对需要返回的文件进行Gzip压缩,或者查找静态资源是否存在已经压缩好的gzip压缩文件。(服务端想办法生成或找到原始文件对应的gzip压缩文件)
  3. 服务端将gzip文件发送给客户端,并且设置相应header中除了包含有Content-Type,Content-Length等字段,还需要添加Content-Encoding:gzip用于说明压缩文件的编码方式。(服务端返回gzip文件并告诉客户端压缩方式为gzip)
  4. 客户端接到Response后,根据Content-Encoding:gzip来对Response 进行解码。 获取到原始response后, 然后显示出网页。(客户端对gzip文件解码)

谁来做压缩?

服务端

  • 如果服务是部署在nginx上,可以使用nginx 开启 gZip 配置即可完成文件压缩。
  • 如果服务区和前端文件中有一层中间应用服务。比如常见的用于进行接口鉴权和文件转发的node服务,也实现服务端的gzip功能,具体如何实现,本人目前还没有实践,个人猜想应该是寻找可用当前服务器语言对应的现成工具实现,如果没有则需要服务端自行完成。

客户端

现在前端流行 spa 应用, 用 react, vue 之类的框架,一般配备 webpack 作为打包工具,webpack有个插件 compression-webpack-plugin 可以对打包文件进行 gZip 等压缩并生成对应的压缩文件。

个人2点想法

  1. 如果服务端做压缩,尽量使用ngnix做
    服务端实现gzip压缩的过程是类似的。
    服务端收到客户端请求后,先会找到对应的文件,
    然后对文件进行压缩,并配置好Content-Encoding 信息,返回压缩后的内容,
  • 如果应用没有上游代理层nginx等,比如服务端就一层 node 就可以直接用自己本身的压缩插件对文件进行压缩。
  • 如果上游配有 nginx 转发处理层,最好交给 nginx 来处理这些,nginx本身就自带gzip压缩功能,毕竟专业的事交给专业的工具做更为放心。
  1. 提前预压缩
    在服务端做gzip压缩,压缩发送在请求过程中,需要耗费更多的CPU和时间,当然类似nginx的压缩可能不一定每次都需要压缩(待调研),但至少第一次压缩是需要的。
    预压缩就是提前把静态文件进行gzip压缩,当请求来的时候,直接读走。这样服务器就没了压缩消耗,而浏览器的解压缩性能相比网络IO,影响不大。
    有一种预压缩方案可以是使用客户端webpack做gzip压缩,然后开启nginx的gzip_static选项(如果服务端不是nginx,实现方案待调研)
    由于只有在上线项目时才打包构建一次,即使在构建时使用*的压缩方式压缩,多耗费一些打包时间根本没任何损耗,这样服务器也不用再去压缩文件,只需要找到相应已经压缩过的文件直接返回就可以了。

总结
能开启 gZip 肯定是要开启的,具体使用在请求时候实时压缩还是在构建时候去生成压缩文件,就要看自己具体业务情况。

大厂在用吗?

了解大厂们是否在使用,是为了知道作为小白的我能否放心使用这项技术的参考之一。
在我写这篇文章时,默默查看了下BAT首页,京东,考拉...都用了gzip压缩,这说明这个技术目前已经普及了。

了解了关于gzip的一些基础问题之后,下面主要演示一些demo小例子,介绍如何在web中应用这项技术。

nginx做压缩应用gzip

如果服务器是部署在nginx,则可以通过修改nginx的配置文件实现在nginx中实现gzip压缩,步骤如下:

1.准备好待测试的静态页面

写一个简单的静态页面,引入vue.js和element-ui.js(用来查看是否有gzip效果)
gzip在web中的应用
打开nginx的配置文件,将nginx的root目录指向这个静态页面所在的目录。
gzip在web中的应用

2.启动gzip配置

在server下打开gzip开关
gzip在web中的应用

3.重启nginx服务

配置好之后,重载nginx配置即可(如果重载不成功,则重启)
这里的命令是mac系统上的nginx重启命令。
sudo brew services reload nginx
sudo brew services restart nginx

效果:文件大小对比

对比没有开启gzip和开启了gzip的文件大小。
可以看到压缩后的html,js,css文件比原始文件小很多,可见这个压缩效果相当可观的。
gzip在web中的应用

webpack做压缩应用gzip

在webpack中主要是通过插件compression-webpack-plugin实现本地打包时的gzip压缩,具体步骤如下:

1.准备好待测试的使用webpack打包的工程

略,读者自行实现。

2.安装配置compression-webpack-plugin

安装插件
npm install compression-webpack-plugin --save-dev

3.修改webpack配置

在webpack配置文件中添加下面几行配置:
const CompressionPlugin = require('compression-webpack-plugin');
...
plugins: [new CompressionPlugin() ]

4.执行本地打包

  • 没有使用compression-webpack-plugin的打包结果: 只有2个文件,build.js是958878
    gzip在web中的应用
  • 使用了compression-webpack-plugin的打包结果:生成了4个文件,build.js是958878,build.js.gz是231189
    gzip在web中的应用

5. 服务端配合测试

使用webpack打包时压缩,需要服务端的配合才能生效的。下面以nginx作为服务端为例,测试如下。
首先让nginx的root指向本地打包的index.html页面;
然后关闭或开启nginx的gzip_static配置对比效果;

  • 关闭gzip_static,重载nginx配置效果如下
    gzip在web中的应用

gzip在web中的应用

  • 开启gzip_static,重载nginx配置
    gzip在web中的应用

gzip在web中的应用

总结和展望

经过上面的分析,再一一回到开头提出的问题

  • gzip压缩之后,我怎么用到项目中呢?
    个人理解是,如果是nginx,需要开启gzip_static配置即可,如果是其它服务器还需要其它服务器自行实现。
  • 后来又在其它地方看到nginx也可以做gzip,那么webpack和nginx的gzip有什么关系吗?
    个人理解是,webpack是一个打包工具,可以打包js,同时通过插件对js文件进行gzip压缩,然后可以将这些文件全部丢给服务端,如果服务部署在nginx则可以做些配置,对.gz文件进行查找,如果找到了则返回.gz给客户端,来代替原始js给客户端,以减少网络传输中的文件大小,如果没找到,也可以配置nginx自己做压缩,然后返回给客户端。因此可以知道nginx可以单独实现gzip,但是webpack需要服务端配合才能实现。

在这个过程中,又出现了几个新的问题,比如

  • 如果是服务不是部署在nginx端,比如部署在node,python,rails,java中的话,如何配合webpack实现gzip呢?
  • 如果启用nginx自己的gzip压缩,对于每次请求,nginx每次都是实时计算压缩文件的吗?个人直觉应该不是吧?如果不是,那么使用nginx直接压缩是不是也很方便呢?nginx自身能否实现预压缩呢?
  • 除了gzip压缩,其它压缩比如Brotli,是不是压缩效果更好呢?
    依然有很多问题需要继续了解...

本人水平有限,请大家多多指教,如有错误,欢迎指正。