Webpack 4 对带有副作用的包有啥期望:假

Posted

技术标签:

【中文标题】Webpack 4 对带有副作用的包有啥期望:假【英文标题】:What Does Webpack 4 Expect From A Package With sideEffects: falseWebpack 4 对带有副作用的包有什么期望:假 【发布时间】:2018-08-16 01:50:43 【问题描述】:

Webpack 4 添加了一个新功能:它现在支持在它捆绑的模块的package.json 中添加sideEffects 标志。

来自Webpack 4: released today

在过去的 30 天里,我们与每个框架密切合作,以确保它们准备好在各自的 cli 等中支持 webpack 4。即使是流行的库,如 lodash-es、RxJS 也支持 sideEffects 标志,所以通过使用他们的最新版本,您会看到即时捆绑包的大小开箱即用。

来自Webpack docs

big-module 的 package.json 中的 "sideEffects": false 标志表示包的模块没有副作用(在评估时)并且只公开导出。这允许 webpack 等工具优化再导出。

虽然第二个链接显示了使用标志的结果,但它并没有清楚地解释什么是副作用。 ES6 包含了here 概述的模块副作用的概念,但这与 Webpack 认为的副作用有什么关系。

sideEffects 标志的上下文中,模块需要避免什么才能毫无问题地使用sideEffects:false,或者相反,模块需要做什么才能毫无问题地使用sideEffects:false

为了完整起见,尽管@SeanLarkin 在下面给出了可靠的答案,但我很想澄清以下内容:

    显然,副作用意味着 fp 中的某些特殊情况,包括日志记录(控制台或其他地方)和抛出错误。我假设在这种情况下这些是完全可以接受的?

    一个模块可以包含循环引用并且仍然使用sideEffects: false吗?

    除了试图追踪误用导致的错误之外,还有什么方法可以验证或模块能够验证模块是否可以sideEffects: false

    是否还有其他因素会阻止模块使用sideEffects: false

【问题讨论】:

【参考方案1】:

来自 webpack 团队的肖恩!我会尽力代替我们仍在制作中的文档来回答您的问题!

根据 ECMA 模块规范(我不会试图找到链接,所以你必须相信我,因为它被埋没了),

每当一个模块重新导出所有导出时,(无论使用或未使用)都需要评估和执行它们,以防其中一个导出与另一个导出产生副作用。

例如,我创建了一个带有照片的小场景以更好地可视化案例:

在这张照片中,我们看到三个模块被导入到一个文件中。然后从该模块重新导出导入的模块:

您可以在这里看到没有一个重新导出是相互影响的,因此(如果给 webpack 一个信号),我们可以忽略导出 bc 甚至被跟踪或使用(大小和构建时间性能优势)。

但是在这种情况下,我们看到导出 c 受到本地状态更改的“影响”,因为它被重新分配给 ba 的总和。因此,(这就是规范要求这样做的原因),我们需要将 ba 及其任何依赖项都包含到包中。

我们选择“sideEffects: false”作为节省编译时间和构建大小的一种方式,因为这使我们能够立即(显式地)修剪开发人员/库作者知道没有副作用的导出(以牺牲package.json 中的属性,或者多 2-3 行配置)。

虽然从技术上讲,这个示例非常原始,但当您开始处理将一堆模块重新导出到更高级别的开发人员体验(Three.js、Angular、lodash-es 等)的框架或库时,那么当您以这种方式标记它们时(如果它们是无副作用的模块导出),性能提升非常显着。

补充说明:

    显然,副作用意味着 fp 中的某些特殊情况,包括日志记录(控制台或其他地方)和引发错误。我假设在这种情况下这些是完全可以接受的?

如果这是试图解决的问题,是的。只要针对模块导出创建的效果不受其他会导致修剪不可接受的影响。

    模块是否可以包含循环引用并仍然使用sideEffects: false?

理论上应该。

    除了尝试追查因误用导致的错误之外,还有其他方法可以验证或验证模块是否能够使用sideEffects: false

据我所知,这将是一个很棒的工具。

    是否还有其他因素会阻止模块使用sideEffects: false

如果属性不在package.json 中或在module.rules 中定义,或者mode: production 未设置(利用优化)。

【讨论】:

非常感谢。请您澄清以下内容以确保完整性: 1. 显然,副作用意味着 fp 中的某些特定内容,包括日志记录(控制台或其他地方)和引发错误。我假设在这种情况下这些是完全可以接受的? 2. 一个模块可以包含循环引用并且仍然使用sideEffects: false吗? 3. 是否有任何其他因素会阻止模块能够使用sideEffects: false 4. 除了试图追踪由其误用引起的错误之外,还有什么方法可以验证或验证模块是否能够使用sideEffects: false 1.如果这是试图解决的问题,是的。只要针对模块导出创建的效果不受其他会导致修剪不可接受的影响。 2. 理论上应该。 3. 如果属性不在 package.json 中或在 module.rules 中定义,或者 mode: production 未设置(利用优化)。 4. 据我所知,这将是一个很棒的工具。 谢谢。我已将其作为附录添加到您的答案中,以便更容易发现。 Webpack 在消除“未使用的导出”时确定它到底丢弃了什么的确切算法是什么?它会只丢弃那些“直接”再出口的imports 吗?或者它会执行一些启发式方法吗?例如。检查正在删除的export 中使用了哪些imports,并可能从代码中删除那些imports。 只需在你的配置文件中设置mode: none 运行webpack,你就可以看到我们所做的注释来显示我们正在跟踪的内容。我们利用了其中的一部分。【参考方案2】:

这个sideEffects 设置非常模糊,文档中没有充分描述。文档大多类似于“没有任何副作用的模块有一个 sideEffects 标志”。

共识是“没有副作用”短语可以被解读为“不与顶层模块外部的事物对话”。

我目前的理解是,这个sideEffects 标志仅用于“再出口”,“再出口”是:

export  a  from './lib/a'
export  b  from './lib/b'

<npm-package>/index.js 中的某个位置(或<npm-package> 中的任何其他文件)。

如果 Webpack 检测到应用程序只从 <npm-package> 导入 a,而没有在任何地方导入 b,那么 Webpack 可以简单地从 <npm-package>/index.js 中删除 export b from './lib/b' 行,从而导致不包括 生成的包中的'./lib/b.js' 文件(这使它比'./lib/b.js' 文件的大小更小)。

现在,如果 './lib/b.js' 有一些***代码行做一些“副作用”,即如果 './lib/b.js' 做了类似的事情:

window.jQuery = ... if (!global.Set) global.Set = require('babel-polyfill').Set new XmlHttpRequest().post('/analytics', data)

那么'./lib/b.js' 将被称为具有“副作用”,因为它的***代码(在import './lib/b' 上执行)会影响'./lib/b.js' 文件范围之外的内容。

同时,只要'./lib/b.js' ***代码没有到达*.js 文件之外,那么它就没有任何“副作用”:

let a = 1
a = a + 1 + computeSomeValue()
export default a
export const b = a + 1
export const c = b + 1

这些都不是“副作用”。

还有一个最后的问题:如果一个 npm 包有任何*.css 用户可以import 的文件,那么这些*.css 文件都是“副作用”,因为:

import 'npm-package/style.css'

没有分配给这个import的变量,这实际上意味着“这个导入的模块没有在应用程序的任何地方使用”对于Webpack。因此,如果 npm-package 具有 sideEffects: false 标志,Webpack 会简单地从包中丢弃 'npm-package/style.css' 文件作为“摇树”过程的一部分。所以,不要写sideEffects: false,而是写"sideEffects": ["*.css"]。即使你的 npm 包不导出任何 CSS 文件,它也可能在将来这样做,这将防止前面提到的“不包含 CSS 文件”错误。

【讨论】:

特别是我的意思是sideEffects相互对抗,即使没有明确使用它也会迫使一个人被拉进来。这不是函数编程参考中的副作用。

以上是关于Webpack 4 对带有副作用的包有啥期望:假的主要内容,如果未能解决你的问题,请参考以下文章

Python常用的包有啥?

Java eclipse中创建项目以后缺省包和命名的包有啥区别?

手机上stream抓包和电脑上的Fiddler抓包有啥不同

python中都有哪些包

javax.persistence,这个包有啥用

fiddler 手机抓包有啥用