我们在打包时,可以采用各种压缩方式,就提高性能。比如Tree Shaking、Http压缩等等。本篇详细介绍各种压缩方式。

Terser

当我们在生产模式打包后,代码会被丑化压缩。
我们知道这是因为当mode:production时,默认就开启了很多配置,其中就包括丑化压缩代码。
在实际开发中虽然我们很少去配置怎么去丑化压缩代码,但是我们也可以学习了解下webpack是用什么工具帮我们实现的。

Webpack 在production模式下,webpack默认会使用很多插件来丑化压缩我们的代码其中最主要的就是TerserPlugin
如果我们想配置自己的terser去覆盖默认的,可以参考如下配置:(在实际开发中,webpack默认开启Terser,一般无需手动配置)

  1. //webpack.config.js
  2. //无需手动npm安装,webpack默认安装好了
  3. const TerserPlugin = require('terser-webpack-plugin');
  4. optimization: {
  5. //minimize设置为true,开启minimizer
  6. minimize: true,
  7. minimizer: [
  8. new TerserPlugin({
  9. //parallel:默认为true,使用多线程并发运行提高构建速度(充分利用多核cpu性能)
  10. parallel: true,
  11. //extractComments:默认true,将注释单独抽到一个文件
  12. extractComments: false,
  13. //设置terser相关配置
  14. terserOptions: {
  15. compress: {
  16. arguments: false,
  17. dead_code: true
  18. },
  19. mangle: true,
  20. toplevel: true,
  21. keep_classnames: true,
  22. keep_fnames: true
  23. }
  24. })
  25. ]
  26. },

关于terser更多的配置规则可参考文档:
https://github.com/terser/terser#compress-options

CSS压缩

在之前学习代码分离中,学习了css代码的分离。我们也可以对分离出来的代码进行压缩。
CSS压缩通常是去除无用的空格等,因为很难去修改选择器、属性的名称、值等;

CSS的压缩我们可以使用另外一个插件:css-minimizer-webpack-plugin;
这个插件的使用非常简单,只需要安装然后直接使用即可。

安装:

  1. npm install css-minimizer-webpack-plugin -D

使用:

//webpack.config.js  
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');  

plugins: [  
  new CssMinimizerPlugin(),  
]

Scope Hoisting(作用域提升)

什么是Scope Hoisting?
我们在之前学习webpack实现模块化原理时,就发现了webpack打包时会写很多闭包对我们的代码进行很多额外的函数作用域封装。

Webpack想要拿到某个函数,可能需要经过很多层的闭包才能拿到。
所以在生产模式下,就可以使用Scope Hoisting进行作用域提升,让webpack打包后的代码更小、运行速度更快

使用:
在production模式下,Scope Hoisting是默认开启的。
如果想要在development模式下开启,需要我们手动配置。配置如下:

//webpack.config.js  
plugins: [  
  new webpack.optimize.ModuleConcatenationPlugin()  
]

Tree Shaking

什么是Tree Shaking?
Tree Shaking的作用就是消除无用代码(dead_code)。
如果一段代码,它既没有副作用又没有被我们使用到,那么这段代码就是dead_code(无用代码)。
我们可以利用Tree Shaking删除掉这些无用代码,来提高我们的性能。

Tree Shaking依赖于ES Module的静态语法分析(不执行任何的代码,就可以明确知道模块的依赖关系)。
而webpack5中,也提供了对部分CommonJS的tree shaking的支持;

实现Tree Shaking的两种方案:
usedExports:通过标记某些函数是否被使用,之后通过Terser来进行优化的;
sideEffects:跳过整个模块/文件,直接查看该文件是否有副作用;

useExports

在前面学习Terser时,我们知道了Terser就是一个对代码进行丑化压缩的工具。
而这里的useExports,就是告诉Terser哪些代码是dead_code,你帮我删掉这些代码。
usedExports会对未使用的代码添加注释(unused harmony export mul),terser根据注释删除代码;

在生产模式下,useExports默认就为true。实际开发中无需手动配置。

//webpack.config.js  
optimization: {  
  // usedExports: 对未使用的代码添加注释(unused harmony export mul),terser根据注释删除代码;  
  usedExports: true,  
  minimize: true,  
  minimizer: [  
    new TerserPlugin({  
      parallel: true,  
      extractComments: false,  
      terserOptions: {  
        compress: {  
          arguments: false,  
          dead_code: true  
        },  
        mangle: true,  
        toplevel: true,  
        keep_classnames: true,  
        keep_fnames: true  
      }  
    })  
  ]  
},

sideEffects(副作用)

sideEffects用于告知webpack compiler哪些模块时有副作用的。
通过sideEffects实现的Tree Shaking更彻底。
useExports只删除没有用到的代码。而sideEffects会删除未用到的代码及其模块化导入的相关代码等。
(在前面学习webpack实现模块化原理时,webpack会将模块化导入导出的方法转化为浏览器认识的代码)

使用:
在package.json文件中,配置sideEffects。
如果我们将sideEffects默认值为false,就是告知webpack可以安全的删除未用到的exports。
如果我们有想要保留的未用到的代码,可以把sideEffects的值写成数组,数组里的文件,声明下这些文件有副作用不要删掉。

//package.json  

 "name": "your-project",  
 //false:全部都没有副作用,都可以删除  
 "sideEffects": false  
 //数组内声明的有副作用,其他的都可以删除  
 "sideEffects": [  
   "./src/some-side-effectful-file.js"  
 ]

实际开发中,我们一般会直接设置package.json里的sideEffects:false;
然后专门针对css文件声明下有副作用,Tree Shaking不要删掉。

 //package.json  
{  
  "name": "your-project",  
  //false:全部都没有副作用,都可以删除  
  "sideEffects": false  
}  

//webpack.config.js  
module: {  
  rules: [  
    {  
      test: /\.css/i,  
      use: ["style-loader", "css-loader"],  
      sideEffects: true,  
    },  
  ],  
},

CSS实现Tree Shaking

如果我们在项目中对css代码进行了代码分离,分离出了独立的文件保存css代码,那我们也可以对css文件进行Tree Shaking。
CSS的Tree Shaking需要借助于其他的插件:PurgeCSS。(帮我们删除未使用的css代码,也支持less,因为它是对打包后的css文件进行处理)

安装:

npm install purgecss-webpack-plugin -D

使用:

//webpack.config.js  
const PurgeCssPlugin = require('purgecss-webpack-plugin');  
//ptah与glob在安装webpack默认安装,无需手动安装  
const path = require("path");  
const glob = require('glob');  

  plugins: [  
    new PurgeCssPlugin({  
      //paths检测哪个路径下的内容需要被分析(PurgeCssPlugin推荐使用golb这个库)  
      // /**/*/表示不处理文件夹  
      paths: glob.sync(`${path.resolve(__dirname,"./src")}/**/*`, {nodir: true}),  
      //由于默认删掉body与html的css代码,这里需要额外配置不删掉  
      safelist: function() {  
        return {  
          standard: ["body", "html"]  
        }  
      }  
    })  
  ]

HTTP压缩

什么是Http压缩?
HTTP压缩是一种内置在 服务器 和 客户端 之间的,以改进传输速度和带宽利用率的方式。

Http压缩及请求流程

  1. 客户端使用webpack将http数据压缩完成后再发送给服务器
  2. 浏览器在想客户端发送http请求(http请求头里包含浏览器兼容的压缩方式Accept-Encoding)
  3. 服务器在知道浏览器兼容的压缩格式后,直接返回对应的压缩后的文件响应(并且响应头里包含压缩格式类型Content-Encoding)

压缩格式特别多,目前比较流行的压缩格式有哪些?

  • gzip : GNU zip格式(定义于RFC 1952)(目前使用最多
  • br – 一种新的开源压缩算法,专为HTTP内容的编码而设计;(能压缩得很小,但是有些浏览器不兼容
  • deflate – 基于deflate算法(定义于RFC 1951)的压缩,使用zlib数据格式封装;

Webpack配置http压缩

我们可以在开发环境下的devServer里配置compress: true,实现了http代码压缩。
但这里主要学习在生产环境下我们使用CompressionPlugin插件进行http压缩。

安装:

npm install compression-webpack-plugin -D

使用:
实际开发中,我们一般只配置test,其它采用默认值即可

//webpack.config.js  
const CompressionPlugin = require('compression-webpack-plugin');  

plugins: [  
  new CompressionPlugin({  
    //哪些文件进行压缩  
    test: /\.(css|js)$/i,  
    //threshold:多大进行压缩、minRatio:压缩比例达到多少进行压缩、algorithm:压缩类型  
    //实际开发中,我们一般只配置test,其它采用默认值即可  
    threshold: 0,  
    minRatio: 0.8,  
    algorithm: "gzip",  
  }),

Html文件中代码的压缩

在之前的学习中,我们可以用到HtmlWebpackPlugin插件来帮我们生成build文件夹下的HTML文件。
但实际上HtmlWebpackPlugin还有一些其他配置,来帮助我们对生成的HTML文件进行代码压缩。

//webpack.config.js  
const HtmlWebpackPlugin = require("html-webpack-plugin");  
plugins: [  
  new HtmlWebpackPlugin({  
    template: "./index.html",  
    // inject: "body"  
    cache: true, // 当文件没有发生任何改变时, 直接使用之前的缓存  
    minify: isProduction ? {  
      removeComments: false, // 是否要移除注释  
      removeRedundantAttributes: false, // 是否移除多余的属性  
      removeEmptyAttributes: true, // 是否移除一些空属性  
      collapseWhitespace: false,  //移除空格  
      removeStyleLinkTypeAttributes: true,    
      minifyCSS: true,  //压缩html内的css  
      minifyJS: {       //压缩html内的js  
        mangle: {  
          toplevel: true  
        }  
      }  
    }: false  
  }),  
],