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

一、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有助于我们减少打包后产物的体积,去掉无用的代码。这里不多说概念,本文的重点还是在于文章标题,我们关注的是webpack的tree-shaking能力以及触发条件。

二、搭建Webpack环境

创建项目

cmd 复制代码
mkdir tree-shaking-webpack && cd tree-shaking-webpack

使用pnpm初始化package.json

cmd 复制代码
pnpm init

安装webpack和webpack-cli

cmd 复制代码
pnpm add webapack webpack-cli -D

创建webpack.config.js配置文件

javascript 复制代码
// webpack.config.js

const path = require("path");

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

修改package.json

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"
  }
}

创建入口文件

javascript 复制代码
// src/main.js

console.log('hello wolrd')

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

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

javascript 复制代码
// 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

1、通过ES6的import和export触发

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

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

我们新增个compute.js文件

javascript 复制代码
// 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方法

javascript 复制代码
const { add, sub } = require("./compute");

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

执行打包后,发现main.js中sub方法也被打打包了

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

javascript 复制代码
// src/compute.js

export default {
  add,
  sub
}

// src/main.js

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

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

所以得出结论,如果要触发tree-shaking,需要通过ES6的import和export触发。

2、通过解构的方法触发

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

javascript 复制代码
import compute from './compute'

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

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

cmd 复制代码
pnpm add lodash-es

修改main.js的内容

javascript 复制代码
// src/main.js

import { get } from "lodash-es";

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

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

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

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

javascript 复制代码
// src/main.js

import _ from "lodash-es";

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

再次打包后,可以看到,bundle.js的体积大小已经来到了恐怖的1.6MB

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

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

3、开启production模式

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

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

javascript 复制代码
// 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方法已经不存在了,而且代码还进行了压缩

4、总结

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

  1. 通过ES6的import和export,包括npm包也必须是ESM
  2. 通过解构的方式引入
  3. 同一文件下,需要开启production模式
相关推荐
可乐ea12 分钟前
多模型编排的三层:框架、模型路由与提供商路由
前端·网络·人工智能·ai智能体·多智能体协作·多模型编排·大模型路由
青柠之夏cc2 小时前
前端拖拽功能原生实现,不引入拖拽库完成业务
开发语言·前端·python
liangshanbo12152 小时前
前端大文件切片上传怎么做?
前端
Sand(ContextGate)3 小时前
Python Agent 测试实战:测试与评估,让 Agent 像传统软件一样可交付
前端·javascript·python·microsoft·ai
广州华水科技3 小时前
单北斗GNSS变形监测系统在城市安全与地质灾害中的应用前景
前端
颜进强3 小时前
20 · NestJs循环依赖与 forwardRef:容器为什么在环面前会死,"占位再补齐"怎么救
前端·后端·ai编程
颜进强3 小时前
19 · NestJs @Global 落地账:装饰器与 `isGlobal` 参数,各在什么场景上岗
前端·后端·ai编程
码艺-Alimjan3 小时前
Tauri 网站To桌面应用实战总结
前端·javascript·vue
颜进强4 小时前
18 · NestJS动态模块与 forRoot:`imports: [ConfigModule.forRoot({...})]` 到底在 import 什么
前端·后端·ai编程
YZ1225524 小时前
【Docker专题】使用Docker部署Vue-Flask项目前后端分离版【前端Docker部署】
前端·vue.js·docker