使用 webpack 开启 tree shaking


作者:Seiya

时间:2019年07月10日


tree shaking


概念

一个模块可能有多个方法,只要其中的某个方法使用到了,则整个文件都会被打到 bundle 里⾯去,tree shaking 就是只把用到的方法打入 bundle ,没用到的方法会在 uglify 阶段被擦除掉。



DCE(Dead code elimination)

也就是把没用用到的 dead code 擦除掉,dead code 的含义具体来讲就是:

  • 代码不会被执行,不可到大;

  • 代码执行的结果不会被用到;

  • 代码只会影响到死变量(只写不读);



tree shaking 原理

tree shaking 本质上就是对模块代码进行静态的分析,在编译阶段确定代码是否被运行,对代码进行注释,并在 uglify 阶段删除无用的代码。


tree shaking 利用了 ES6 模块的以下特点:

  • 只能作为模块顶层的语句出现;

  • import 的模块名只能是字符串常量;

  • import bindingimmutable 的(不可修改);


注意:

tree shaking 利用的是 es 的模块系统,所以要实现第三方库的 tree shaking,需要安装库对应的模块系统即可。



JS tree shaking


使用

在 webpack v4 中,不再需要配置 UglifyjsWebpackPlugin,只需要配置mode为"production",即可显式激活 UglifyjsWebpackPlugin 插件。具体配置如下:

const path = require("path");

module.exports = {
  entry: {
    app: "./src/app.js"
  },
  output: {
    publicPath: __dirname + "/dist/",
    path: path.resolve(__dirname, "dist"),
    filename: "[name].bundle.js"
  },
  mode: "production"
};

接下来,我们新增一个 util.js 文件,并输入一下内容:

export function a() {
  return 'this is function "a"';
}

export function b() {
  return 'this is function "b"';
}

export function c() {
  return 'this is function "c"';
}

然后,我们在 app.js 文件中引入 util.js 中的部分函数,如下所示:

import { a } from "./vendor/util";
console.log(a());

最后,我们执行打包命令,可以发现,只有 a() 函数的内容被打包进来了,其它函数并没有被打包进来,说明 tree shaking 配置成功。



处理第三方库

对于经常使用的第三方库(例如 jQuery、lodash 等等),如何实现Tree Shaking?下面以 lodash.js 为例,进行介绍。


首先,我们在 app.js 文件中引入

import { chunk } from "lodash";
console.log(chunk([1, 2, 3], 2));

然后,我们安装 lodash 库的 ES 写法版本:npm install -D lodash-es。

接下来,我们修改 app.js 文件,如下所示:

import { chunk } from "lodash-es";
console.log(chunk([1, 2, 3], 2));

提示:

在一些对加载速度敏感的项目中使用第三方库,请注意库的写法是否符合 es 模板系统规范,以方便 webpack 进行 tree shaking



css tree shaking


随着 webpack 的兴起,css 也可以进行 Tree Shaking: 以去除项目代码中用不到的 CSS 样式,仅保留被使用的样式代码。要实现 css tree shaking,需要借助几个插件:

  • PurifyCSS:仅保留被使用的 css 样式代码

  • glob-all:帮助 PurifyCSS 进行路径处理,定位要做 Tree Shaking 的路径文件


接下来,我们结合 extract-text-webpack-plugin 的配置编写 webpack 配置文件:

const path = require("path");
const PurifyCSS = require("purifycss-webpack");
const glob = require("glob-all");
const ExtractTextPlugin = require("extract-text-webpack-plugin");

let extractTextPlugin = new ExtractTextPlugin({
  filename: "[name].min.css",
  allChunks: false
});

let purifyCSS = new PurifyCSS({
  paths: glob.sync([
    /* 要做CSS Tree Shaking的路径文件 */
    path.resolve(__dirname, "./*.html"),  // 请注意,我们同样需要对 html 文件进行 tree shaking
    path.resolve(__dirname, "./src/*.js")
  ])
});

module.exports = {
  entry: {
    app: "./src/app.js"
  },
  output: {
    publicPath: __dirname + "/dist/",
    path: path.resolve(__dirname, "dist"),
    filename: "[name].bundle.js",
    chunkFilename: "[name].chunk.js"
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract({
          fallback: {
            loader: "style-loader",
            options: {
              singleton: true
            }
          },
          use: {
            loader: "css-loader",
            options: {
              minimize: true
            }
          }
        })
      }
    ]
  },
  plugins: [extractTextPlugin, purifyCSS]
};
最后更新时间: 2019-7-16 12:54:06