likes
comments
collection
share

讲一讲Webpack tree-shaking的触发机制

作者站长头像
站长
· 阅读数 6

一、tree-shaking是什么?

这个词应该大家都不陌生了,翻译过来就是“树摇”。

讲一讲Webpack tree-shaking的触发机制

看一下官方的解释:

Tree shaking is a term commonly used in the JavaScript context for dead-code elimination. It relies on the static structure of ES2015 module syntax, i.e. import and export. The name and concept have been popularized by the ES2015 module bundler rollup.

简言之,tree-shaking有助于我们减少打包后产物的体积,去掉无用的代码。这里不多说概念,本文的重点还是在于文章标题,我们关注的是webpacktree-shaking能力以及触发条件。

二、搭建Webpack环境

创建项目

mkdir tree-shaking-webpack && cd tree-shaking-webpack

使用pnpm初始化package.json

pnpm init

安装webpackwebpack-cli

pnpm add webapack webpack-cli -D

创建webpack.config.js配置文件

// webpack.config.js

const path = require("path");

module.exports = {
  entry: "./src/main.js",
  output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "bundle.js",
  },
};

修改package.json

{
  "name": "tree-shaking-webpack",
  "version": "1.0.0",
  "description": "",
  "main": "./src/main.js", // 修改入口
  "scripts": {
    "build": "webpack --config ./webpack.config.js" // 增加build命令
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^5.89.0",
    "webpack-cli": "^5.1.4"
  }
}

创建入口文件

// src/main.js

console.log('hello wolrd')

执行build命令,可以看到已经打包成功了

讲一讲Webpack tree-shaking的触发机制

但是控制台报了个警告,原因是没有配置mode,我们配置mode为开发模式development即可

// webpack.config.js

const path = require("path");

module.exports = {
  mode: "development", //开发模式
  entry: "./src/main.js",
  output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "bundle.js",
  },
};

再次执行build,控制台不再报错

讲一讲Webpack tree-shaking的触发机制

三、webpack的tree-shaking

1、通过ES6的import和export触发

根据前面tree-shaking介绍中提到,tree-shaking其实依赖了ES6中的importexport

所以如果我们先做第一个尝试:commonJS是否支持tree-shaking

我们新增个compute.js文件

// src/compute.js

const add = (a, b) => {
  return a + b;
};

const sub = (a, b) => {
  return a - b;
};

module.exports = {
  add,
  sub,
};

main.js中引用compute.js,并且只使用add方法

const { add, sub } = require("./compute");

console.log(add(1, 2));

执行打包后,发现main.jssub方法也被打打包了

讲一讲Webpack tree-shaking的触发机制

我们换成ES6规范的导入和导出

// src/compute.js

export default {
  add,
  sub
}

// src/main.js

import { add, sub } from './compute.js'

执行打包,main.js中不再出现sub方法

讲一讲Webpack tree-shaking的触发机制

所以得出结论,如果要触发tree-shaking,需要通过ES6importexport触发。

2、通过解构的方法触发

在满足第一点的条件下,我们试想一下,如果我们是import整个对象,是否会触发tree-shaking呢?例如:

import compute from './compute'

console.log(compute.add(1,2))

因为这个案例的打包文件体积比较小,我们看不太出来,我们尝试安装一个lodash来试一下,需要注意的是,我们需要安装的是lodashESM版本,即lodash-es,否则官方默认的lodashcommonJS规范

pnpm add lodash-es

修改main.js的内容

// src/main.js

import { get } from "lodash-es";

console.log(get({ a: 1 }, "a"));

执行打包,并创建index.html文件引入打包后的bundle.js

讲一讲Webpack tree-shaking的触发机制

打开index.html,查看bundle.js的体积大小为103kb

讲一讲Webpack tree-shaking的触发机制

如果我们不使用解构的方式,直接引入整个lodash,然后打包

// src/main.js

import _ from "lodash-es";

console.log(_.get({ a: 1 }, "a"));

再次打包后,可以看到,bundle.js的体积大小已经来到了恐怖的1.6MB 讲一讲Webpack tree-shaking的触发机制

经过tree-shaking之后,bundle.js只打包了lodashget方法,其他无关的方法都被“摇走了”

讲一讲Webpack tree-shaking的触发机制

所以得出结论,如果要触发tree-shaking,需要通过解构的方式触发。

3、开启production模式

细心的同学其实可以发现,上面第一点案例中,通过import的方式导入了compute.js文件,main.js代码中虽然没有了sub方法,但是compute.js依然会把sub方法打包

讲一讲Webpack tree-shaking的触发机制

这是因为webpack对同一文件中的tree-shaking必须要开启production模式才能生效

// webpack.config.js

const path = require("path");

module.exports = {
  mode: "production", //生产模式
  entry: "./src/main.js",
  output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "bundle.js",
  },
};

再次执行打包,发现sub方法已经不存在了,而且代码还进行了压缩

讲一讲Webpack tree-shaking的触发机制

4、总结

通过上面的几个案例,webpack如果要实现tree-shaking必须满足以下几个条件:

  1. 通过ES6importexport,包括npm包也必须是ESM
  2. 通过解构的方式引入
  3. 同一文件下,需要开启production模式