Webpack中的tree-shaking

2023-11-19

Webpack中的tree-shaking

tree-shaking就是把js文件中无用的模块或者代码删掉。而这通常需要借助一些工具。在webpack中tree-shaking就是在打包时移除掉javascript上下文中无用的代码,,从而优化打包的结果。

在webpack5中已经自带tree-shaking功能,在打包模式为production时,默认开启 tree-shaking功能。

简单实践

使用webpack5来进行一次简单的实践。

首先创建一个文件夹作为项目的根目录,然后运行

npm init -y

接着安装一下webpack和webpack-cli

npm install webpack webpack-cli --save-dev

然后创建一个src文件夹,在里面创建两个js文件index.jsutils.js

index.js

import tool from './util.js'

// 使用a变量,调用文件里面的a函数,不使用b函数
console.log(tool.add(1, 2))
console.log('hello world')

// 不可能执行的代码
if (false) {
    console.log('这是一段不可能执行的代码')
}

// 定义了但是没用的变量
const m = 1

utils.js

function add(a, b) {
    console.log(a, b)
    return a + b
}
function count(num) {
    console.log(num)
    return num++
}
export default {
    add,
    count
}

配置一下package.json的启动命令:

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack"
},

然后进入项目根目录,在控制台运行npm run build,默认的打包模式就是production,之后就会在根目录下生成dist文件夹,其中的main.js就是打包后的结果:

在这里插入图片描述

外面可以发现导入但没有使用的count函数、定义未使用的m变量还有不可能执行的代码都被shaking掉了。

副作用sideEffects

什么是副作用呢?这其实是针对与模块来说的,也就是一个js文件。我们说一个js文件有副作用,指的是这个js文件中有一些代码执行之后会对模块之外的代码产生影响。例如我们在js文件中进行pollify(打补丁),或者进行类似Array.prototype.add = ...的行为都会产生副作用。

那么针对于这些有副作用的代码,webpack会帮我们打包进来吗?如果它是一个合格的打包工具,那么我想它是会这样做的,而事实也是如此。

实践一下看看,修改一下刚刚的util.js文件,增加下面的代码:

Array.prototype.side = function () {
    console.log('这是一段有副作用的代码')
}

我们在Array的原型上挂载了一个side方法,运行npm run build打包得到以下结果(格式化):

;(() => {
    'use strict'
    Array.prototype.side = function () {
        console.log('这是一段有副作用的代码')
    }
    const o = function (o, l) {
        return console.log(o, l), o + l
    }
    console.log(o(1, 2)), console.log('hello world')
})()

可以看到webpack确实帮我们打包进来了。

那么webpack是怎么判断一段代码是否有副作用的呢?原来它是借助了terser这个库。terser是一个ES6+的js解析器、处理程序和代码压缩工具,webpack正是依赖这个库进行js的解析和代码压缩。

当使用import导入一个模块时,由于ES6module的静态结构特性,webpack可以首先判断一下是否使用了导入的代码,如果没有,它会直接不导入该模块,这样就不用terser进入模块去分析里面的代码。

如果不做额外的配置,导入一个模块但不适用该模块导出的内容,此时webpack还是会用terser去分析该模块代码是否有副作用。

实践一下看看是不是这样。

我们修改一下index.js的代码,导入模块但是不使用。

import tool from './util.js'

console.log('hello world')

// 不可能执行的代码
if (false) {
    console.log('这是一段不可能执行的代码')
}

// 定义了但是没用的变量
const m = 1

util.js的代码不变,如下:

function add(a, b) {
    console.log(a, b)
    return a + b
}
function count(num) {
    console.log(num)
    return num++
}

Array.prototype.side = function () {
    console.log('这是一段有副作用的代码')
}
export default {
    add,
    count
}

打包结果:

;(() => {
    'use strict'
    ;(Array.prototype.side = function () {
        console.log('这是一段有副作用的代码')
    }),
        console.log('hello world')
})()

不错不错,确实如此。虽然util模块被导入没有使用,但是webpack还是对该模块做了副作用的分析和评估。

那么这样会不会有上面问题呢?

设想一下有一个模块被导入了,但是我们并没有使用该模块,且我们明确地直到该模块中的代码没有副作用,是一个纯正的ES Module,但是webpack还是会对该模块进行分析评估的工作,这就导致了打包时间的增加。所以强大的webpack允许我们配置这一行为。

sideEffects配置项

false

webpack允许我们声明一个模块有无副作用。

如果所有模块都是纯正的,即都没有副作用,我们可以设置

"sideEffects": false  

那么如果导入的模块没有使用,webpack就不会导入它,更不会去分析它。

怎么验证呢?我们直接在package.json文件中添加"sideEffects": false,然后util.js还是保留那句副作用的代码:

Array.prototype.side = function () {
    console.log('这是一段有副作用的代码')
}

在index.js中导入util.js但是不使用。打包结果如下:

;(() => {
    'use strict'
    console.log('hello world')
})()

可以看到那句有副作用的代码并没有被打包,说明webpack直接跳过了util模块的副作用分析。

数组

sideEffects配置项其实更多是用来声明需要进行副作用分析的模块的。因为在实际开发中不可能所有模块都没有副作用。

此时sideEffects的值是一个数组,数组中包含的文件告诉webpack在遇到这些模块的导入时要进行副作用的分析评估。

而没有指明的模块,在导入后如果没有被使用,那么webpack不会将它导入,更不会对他进行分析。

我们修改一下package.json中sideEffects的值如下:

"sideEffects": [
    "**/*.css"
]

其他代码和之前一样,我们看看webpack会不会去分析util,结果和上面的一样,那段有副作用的代码没有被打包。

结论

webpack官方文档总结的已经非常好了,这里就引用一下把!

我们学到为了利用 tree shaking 的优势, 你必须…

  • 使用 ES2015 模块语法(即 importexport)。
  • 确保没有编译器将您的 ES2015 模块语法转换为 CommonJS 的(顺带一提,这是现在常用的 @babel/preset-env 的默认行为,详细信息请参阅文档)。
  • 在项目的 package.json 文件中,添加 "sideEffects" 属性。
  • 使用 mode"production" 的配置项以启用更多优化项,包括压缩代码与 tree shaking。

备注:本文参考webpack5中文文档结合自己的实践理解所写,可能会有错误,欢迎讨论。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Webpack中的tree-shaking 的相关文章

  • 如何在chart.js中使用JSON数据?

    您好 我一直在尝试使用 MYSQL 数据库中的数据 并使用它们通过 Chart js 创建图形图表 我将数据编码为 JSON 数据 通过 php 文件名 data1 php 现在我需要使用 Jquery 或 javascript 将这些 J
  • while 循环中 regex.exec() 赋值的更好解决方案

    这里有更好的解决方案吗 我尽量避免里面的作业while但仍然能够循环匹配并使用捕获的组 var match var CSS URL PATTERN url s s s s gm while match CSS URL PATTERN exe
  • 禁用小写字符下划线:g q p j y?

    有时您不希望下划线盲目地穿过带下划线的页面标题 有没有办法自动优雅地禁用强调对于某些小写字符 在这些情况下 最好不要在这些小写字母下划线 例如 g q p j y CSS h1 text decoration underline PAGE
  • 如何在 Adob​​e Brackets 中使用 const 和 let 禁用 JSLint ES6 错误?

    我用 Google 搜索并浏览了这个网站 但我只能找到 JSHint 而不是 JSLint 的答案 为了摆脱 use function form of use strict 错误 我添加了 jslint node true 但要禁用使用错误
  • Firebase HTTP Cloud Functions - 读取数据库一次

    我有 Firebase HTTPs 功能 该函数需要根据查询参数从 Firebase 数据库读取值 并根据该数据返回结果 Firebase JS SDK 表示要使用以下命令来执行此操作 return firebase database re
  • 如何清除画布元素中的多边形区域?

    我使用过clearRect函数 但没有看到多边形的等效函数 我天真地尝试过 ctx fillStyle transparent ctx beginPath ctx moveTo 0 0 ctx lineTo 100 50 ctx lineT
  • 如何在html中创建字体选择栏

    我想创建一个下拉菜单 在其中我们可以看到所有可用的字体 并且可以选择我们选择的任何字体 我还想创建一个字体颜色选择小部件 存在大量的字体样式 我想知道如何获取所有这些字体以及如何创建一个小部件 用户可以使用该小部件选择他选择的颜色 为了创建
  • Karma + JSPM + Typescript - 未找到“.ts.js”

    主要只是想让 Karma JSPM 在加载 ts 文件时发挥良好作用 但绝对没有运气 我看到一个讨论库 https github com Larchy karma jspm typescript coverage tree master一个
  • 异步 JS 加载到 head 中

    我需要将脚本异步加载到页面上 我正在使用createElement方法在头部动态插入脚本标签 发生的事情是首先加载页面源 完成后 头部中包含的所有元素都会并行加载 一旦全部加载完毕 我动态添加的脚本就会加载 这在逻辑上是有道理的 但我正在寻
  • 代理递归函数

    想象一个简单的递归函数 我们试图包装它以检测输入和输出 A simple recursive function const count n gt n 1 count n 1 Wrap a function in a proxy to ins
  • CKEDITOR.styleSet.add - 'a' 元素的新样式

    由于某种原因如果我改变 p to a 它不再出现在样式列表中 有什么理由吗 CKEDITOR stylesSet add default name Wys wiersza 1 element p styles line height 18p
  • 使用 ECMA 脚本向节点(页面)添加新属性

    我需要在页面激活时向页面添加属性 我决定建立一个工作流程 在激活步骤之前执行相同的操作 我的自定义工作流程步骤 激活步骤之前的步骤 使用 ECMA 脚本来实现此目的 这是我到目前为止所拥有的 var workflowData granite
  • 如何使用Javascript获取ASP.NEt Web Forms标签的值?

    我有以下标签控件
  • 删除数组中所有对象的属性

    我想删除bad数组中每个对象的属性 有没有比使用更好的方法for循环并从每个对象中删除它 var array bad something good something bad something good something for var
  • 当用户输入/删除时,使文本字段中的提示消失/重新出现[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 有谁知道我怎样才能在我的搜索栏中做出
  • 如何使用 Soundcloud api 将流传输到 html5 音频播放器中?

    我刚刚开始学习 javascript 作为我的第一次尝试 我想创建自定义音频播放器 它使用 soundcloud 的 api 作为音乐源 到目前为止 这就是我的设置
  • Angular2 - 防止复选框被选中

    我有一个每行包含一个复选框的表 在表头中 我有一个Check All切换所有表格行框的复选框 我正在尝试实现一些逻辑 如果复选框的数量将超过特定限制 则显示错误并且不切换表行复选框或checkall盒子本身 有一个问题允许checkAll即
  • 如何优化 Three.js 中多个 sphereGeometry 的渲染?

    我想优化 Three js 中 sphereGeometry 的渲染 因为它成为我的程序的瓶颈 javascript程序如下所示 var sphereThree for var idSphere 0 idSphere lt numSpher
  • 将base64图像转换为Node Js中的文件

    我是 Node Js 新手 我需要包含用户的个人资料图片 我从 IOS 应用程序收到 Base64 图像的请求 我需要将其存储在 images 文件夹中并将图像路径保存在 mongodb 数据库中 我使用了以下代码 var bitmap n
  • 如何在 Servlet 中打开弹出窗口,然后重定向页面

    我想在调用 servlet 时打开一个弹出窗口 然后想将 servlet 重定向到某个 jsp page 这就是我所做的 protected void doGet HttpServletRequest request HttpServlet

随机推荐

  • 除了快手与抖音,“云想科技们”也在加速“出圈”

    在电商行业 大家可能听说过传统电商行业的 代运营 现在短视频赛道崛起 代企业运营 以效果为前提 做出符合企业品牌价值好的 新的内容的短视频营销服务商也受到更多关注 云想科技就是其中的代表 在行业新常态下 布局新业务依旧是企业寻求增长新动力的
  • Visual Grounding任务常用数据集介绍RefCOCO、RefCOCO+、RefCOCOg、ReferItGame和Flickr30K Entities

    Visual Grounding任务常用的数据集有五个 RefCOCO RefCOCO RefCOCOg ReferItGame和Flickr30K Entities RefCOCO RefCOCO RefCOCOg 是三个从MSCOCO中
  • linux服务器使用gustat指令查看gpu显卡的使用情况(比nvidia-smi好用)

    使用gpustat指令需要先安装gpustat 安装需要root权限 apt install gpustat 查看gpu使用情况 gpustat nvidia smi gpustat相比于nvidia smi查看的信息更加详细 可以看到使用
  • flex布局宽高度设置不成功

    flex布局中 会出现是在宽高 但是不起作用 那是因为flex布局当不够的时候自动压缩了 可以选择 让其不压缩 flex shrink 0 然后在设置宽高 或者利用复合属性 flex 0 0 83rpx 这篇博客是对flex布局的讲解 以及
  • 如何给证件照换一个背景颜色

    我们在考试报名的时候 经常是不同的考试需要不同的登记照尺寸和背景颜色 但是我们基本上不可能每种颜色的证件照都去拍一张吧 那样也太麻烦成本也太高 所以通过前端实现了一个改变证件照背景颜色的方法 他可以将证件照的背景颜色修改为任意的颜色 而不局
  • 微信小程序支付中的prepay_id获取方法,以及微信支付统一签名算法闭坑

    class Wechat 公众号的或者小程序支付参数 private appId private appSecret 商家的配置信息 private mch id private mch key 回调地址 public notify url
  • 前台与后台数据交互

    后台取到的数据在前台应该用EL表达式显示 xx xx 如果要给前台单选按钮赋值 需要用js进行判断 满足条件后 id attr checked true false 设置选中状态 设置单选按钮及下拉列表不可用是disabled disabl
  • for循环之斐波拉契数列

    题目大意 菲波那契数列是指这样的数列 数列的第一个和第二个数都为1 接下来每个数都等于前面2个数之和 给出一个正整数k 要求菲波那契数列中第k个数是多少 Input 输入一行 包含一个正整数k 1 lt k lt 46 Output 输出一
  • 在C++中调用OpenSSL库进行编程

    目录 OpenSSL简介 下载OpenSSL库并配置实验环境 OpenSSL库的加密函数的认识 使用 EVP 库实现 DES 和 AES 加密 EVP EncryptUpdate 函数参数详解 OpenSSL加密实践 RSA 密钥生成 RS
  • 第一次面试前端实习生心得

    今天第一次去面试前端岗位的实习生 公司规模不算大吧 不过也有好几个部门 说说我的面试心得吧 首先是hr面 如下是她问的问题 学这个多久了 大四还有没有课 是否只需完成毕业设计就行了不用上课 同学暑假都在干嘛 学校课程学了什么 同学们主要找哪
  • (转载)我们需要什么样的字段类型

    数据库定义到char类型的字段时 不知道大家是否会犹豫一下 到底选char nchar varchar nvarchar text ntext中哪一种呢 结果很可能是两种 一种是节俭人士的选择 最好是用定长的 感觉比变长能省些空间 而且处理
  • Java 两种zero-copy零拷贝技术mmap和sendfile的介绍

    详细介绍了两种zero copy零拷贝技术mmap和sendfile的概念和基本原理 目录 1 标准IO 2 零拷贝 2 1 sendfile调用 2 1 mmap调用 2 2 MQ中的应用 1 标准IO 很多软件是基于server cli
  • MES在流程和离散型制造企业的应用存在哪些差别?

    企业的生产方式 主要可以分为按定单生产 按库存生产或上述两者的组合 从生产类型上考虑 则可以分为批量生产和单件小批生产 从产品类型和生产工艺组织方式上 企业的行业类型可分为流程生产行业和离散制造行业 典型的流程生产行业有医药 石油化工 电力
  • C++ 拷贝构造函数和赋值运算符

    本文主要介绍了拷贝构造函数和赋值运算符的区别 以及在什么时候调用拷贝构造函数 什么情况下调用赋值运算符 最后 简单的分析了下深拷贝和浅拷贝的问题 拷贝构造函数和赋值运算符 在默认情况下 用户没有定义 但是也没有显式的删除 编译器会自动的隐式
  • Direct3D光照

    光照的组成 环境光 这种类型的光经其他表面反射到达物体表面 并照亮整个场景 要想以较低代价粗略模拟这类反射光 环境光是一个很好的选择 漫射光 这种类型光沿着特定的方向传播 当它到达某一表面时 将沿着各个方向均匀反射 无论从哪个方位观察 表面
  • Spring的资源访问接口---Resource

    JDK提供的访问资源的类 File等 不能很好满足各种某些资源的访问需求 比如缺少从类路径和Web容器的上下文中获取资源的资源操作类 Spring的Resource接口提供了更好用的资源访问能力 Spring使用Resource访问各种资源
  • linux 交叉编译找不到库文件

    网上大众的作法这里不做介绍 在编译一些库的时候可能某些工具会用到宿主机的 而这些工具在调用的时候会找和宿主机匹配的库 而交叉编译的库是不能用的 遇见这种情况基本是解决不了的 但是可以通过configure 的选项去掉这些过程 举个例子 我在
  • MySQL多列字段去重的案例实践

    同事提了个需求 如下测试表 有code cdate和ctotal三列 select from tt 现在要得到code的唯一值 但同时带着cdate和ctotal两个字段 提起 唯一值 想到的就是distinct distinct关键字可以
  • Nodejs版本管理工具mvn部署

    部署mvn curl o https raw githubusercontent com creationix nvm v0 34 0 install sh bash 添加环境变量 vim zshrc export NVM DIR HOME
  • Webpack中的tree-shaking

    文章目录 Webpack中的tree shaking 简单实践 副作用sideEffects sideEffects配置项 false 数组 结论 Webpack中的tree shaking tree shaking就是把js文件中无用的模