这篇文章上次修改于 372 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

webpack的深度Tree-shaking

什么是Tree-shaking?

Tree-shaking就是的意思,在通俗的讲就是把没用的模块抖掉,用于打包时消除无用的代码,无用就是字面意思,没有用到的模块。

为什么需要Tree-shaking?

随着项目的代码量逐渐增多,可能有没用的或产生副作用的模块代码嵌入到项目中,这样就影响了我们上线产品的质量和性能。所以需要在打包上线的过程中,清除掉项目中没用到的代码模块。

webpack的深度Tree-shaking

Webpack 4 自带的有Tree-shaking的功能,只能在production环境下打包才能起作用,但有不足,它无法Tree-shaking掉有副作用的模块,也可以说它无法读取模块的作用域[[scope]],所以无法对其Tree-shaking。

js的深度Tree-shaking

举个Webpack 4能Tree-shaking的例子:

现在有个模块demo.js

const sync = () => {
  console.log('sync');
}

const isToArray = (args) => {
  return Object.prototype.toString.call(args) === '[object Array]';
}

export {
  sync,
}

再入口文件index.js导入

import './demo.js';

console.log('index');

development环境打包,没有tree-shaking掉demo模块isToArray方法

production环境打包,因为isToArray方法的父作用域是当前模块,所以成功!

那换种方式,这次引入lodash-es只引用其一个isArray属性方法

import lodash from 'lodash-es';

const sync = () => {
  console.log('sync');
}

const isToArray = (args) => {
  console.log(lodash.isArray(args)); // isArray引用
}

export {
  sync,
  isToArray
}

下面production打包

看打完包main.js的大小(86.5kb),就知道webpack虽然掉了isToArray方法,却将整个lodash-es模块全部都打包了,这是因为lodash-es的子模块父作用域并不属于当前模块,所以不能进行深度遍历进行Tree-shaking,这肯定不是我们想要的,这时到我们的主角登场了。

webpack-deep-scope-plugin

webpack-deep-scope-plugin是一位中国同胞(学生)在Google夏令营,在导师Tobias带领下写的一个webpack插件。

插件主要用于填充webpack自身Tree-shaking的不足,通过作用域分析来消除无用的代码。

首先安装该插件

npm i webpack-deep-scope-plugin --save-dev

然后在webpac里进行配置

const WebpackDeepScopeAnalysisPlugin = require('webpack-deep-scope-plugin').default;
 
module.exports = {
  ...,
  plugins: [
    ...,
    new WebpackDeepScopeAnalysisPlugin(),
  ],
}

ok,现在重新看一下打包结果

看看大小由原来的的86.5kb减到975b,厉害!!!

小结

1、lodashlodash-es的区别?

lodash-es支持按需引入,并且支持tree-shaking,前提是要按需引入,举个例子

import { isArray } from 'lodash-es'; //    <-----

const sync = () => {
  console.log('sync');
}

const isToArray = (args) => {
  console.log(isArray(args)); // isArray引用
}

export {
  sync,
  isToArray
}

这样在打包时除了isArray方法,lodash-es其余属性模块都会被Tree-shaking,注意:这是webpack 4支持的,一般我们lodash-esTree-shaking搭配使用,不用配置插件就可Tree-shaking。

2、webpack-deep-scope-plugin只支持生产环境打包Tree-shaking,不支持开发环境,因为开发时也不需要打包。

css的深度Tree-shaking

删除项目中没用到的css样式,这个没啥说的,配置完只管使就好了,同样需要个插件

purifycss-webpack

先安装包

npm i purifycss-webpack purify-css --save-dev

要使用这个插件需要先将css从打包后的文件抽离出来这是需要安装和配置mini-css-extract-plugin插件

mini-css-extract-plugin不能和style-loader联用,替换即可

npm install --save-dev mini-css-extract-plugin

然后配置

const path = require('path');
const glob = require('glob');
const PurifyCSSPlugin = require('purifycss-webpack');
 
module.exports = {
  ......,
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
          },
          'css-loader',
        ],
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[id].css',
    }),
    new PurifyCSSPlugin({
      paths: glob.sync(path.join(__dirname, './dist/*.html')), // 定义需要处理的html文件
    })
  ]
};

配置完成,现在css就能进行Tree-shaking了。

总结

webpack的原理,其实就是遍历所有的模块,把它们打包成一个文件,在这个过程中,它就知道哪些export的模块有被使用到。那我们同样也可以遍历所有的scope(作用域),对没有用到的scope进行Tree-shaking,最后只留下我们需要的。