Webpack 4添加了一项新功能:它现在支持捆绑的模块sideEffects
中的package.json
标记。
在过去的30天里,我们与每个框架密切合作,以确保他们已经准备好在各自的cli等中支持webpack 4.即使是像lodash-es这样流行的库,RxJS也支持sideEffects标志,所以通过使用他们的最新版本,您将看到即时捆绑尺寸开箱即用。
“sideEffects”:big-module的package.json中的false标志表示包的模块没有副作用(评估时),只暴露出口。这允许像webpack这样的工具优化再出口。
虽然第二个链接显示了使用该标志的结果,但它没有清楚地解释什么构成副作用。 ES6包括概述here模块的副作用概念,但这与Webpack考虑副作用有何关联。
在sideEffects
标志的上下文中,模块需要避免使用sideEffects:false
而没有问题,或者说对话,模块需要做什么才能使用sideEffects:false
没有问题。
为了完整起见,尽管@ SeanLarkin在下面给出了坚实的答案,但我很乐意澄清以下内容:
显然,副作用意味着fp中特有的东西,包括日志记录(控制台或其他地方)以及抛出错误。我假设在这种情况下这些是完全可以接受的吗?
模块是否可以包含循环引用并仍然使用sideEffects: false
?
是否有任何方法可以验证或模块是否能够验证模块是否sideEffects: false
除了尝试追踪因误用而导致的错误?
是否还有其他因素阻止模块使用sideEffects: false
?
答案 0 :(得分:44)
来自webpack团队的肖恩!我会尽力代替我们仍在进行中的文档来回答您的问题!
根据ECMA模块规范(我不打算尝试找到链接,所以你必须相信我,因为它被埋没了),
每当模块重新导出时,如果其中一个导出与另一个导出产生副作用,则需要评估并执行所有导出(无论是否使用)。
例如,我创建了一个带有照片的小方案,以更好地可视化案例:
在这张照片中,我们看到具有单个导入的3个单独模块被导入单个模块,然后获取这些默认导出,从该模块重新导出:
你可以在这里看到,没有一个reexport会受到彼此影响,因此(如果给webpack一个信号),我们可以省略导出b
和c
甚至被跟踪或使用(大小)并建立时间性能优势)。
但是在这种情况下,我们发现导出c
受到本地状态更改的“影响”,因为它被重新分配为b
和a
的总和。因此,(这就是规范要求的原因),我们需要将b
和a
及其任何依赖项都包含在bundle中。
我们选择“sideEffects:false”作为节省编译时间和构建大小的方法,因为这允许我们立即修剪(显式)导出开发人员/库作者知道副作用免费的导出(代价是package.json中的属性,或2-3行的配置)。
虽然从技术上讲这个例子非常原始,但是当你开始处理Frameworks或Libraries,它们为开发者体验(Three.js,Angular,lodash-es等)重新出口一大堆模块时,性能提升当(如果它们是sideEffect free模块导出)你以这种方式标记它们时是很重要的。
其他说明:
- 显然,副作用意味着fp中特有的东西,包括日志记录(控制台或其他地方)以及抛出错误。我假设在这种情况下这些是完全可以接受的吗?
醇>
在这是试图解决的情况下,是的。只要对模块出口产生的影响不受其他会导致修剪不可接受的影响。
- 模块是否可以包含循环引用,仍然使用
醇>sideEffects: false?
理论上它应该。
- 是否有任何方法可以验证或模块是否能够使用
醇>sideEffects: false
,而不是尝试追踪因滥用而导致的错误?
不是我所知道的,但这将是一个很棒的工具。
- 是否有任何其他因素会妨碍模块使用
醇>sideEffects: false
?
如果该属性不在package.json
中或在module.rules
中定义,或者mode: production
未设置(利用优化)。
答案 1 :(得分:9)
此sideEffects
设置非常含糊,在文档中没有充分描述。这些文档大多是“没有任何副作用的模块的sideEffects
标志”。
一致认为,“没有副作用”的短语可以被解除为“不与顶级模块外部的事物交谈”。
我目前的理解是,这个sideEffects
标志仅用于“再出口”,“再出口”是:
export { a } from './lib/a'
export { b } from './lib/b'
<npm-package>/index.js
中的某个地方(或<npm-package>
内的任何其他文件)。
如果Webpack检测到应用程序仅从a
导入<npm-package>
,并且未在任何位置导入b
,则Webpack可以简单地从{export { b } from './lib/b'
行删除<npm-package>/index.js
行1}}导致不包括
生成的包中的'./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'
文件范围之外的内容。< / p>
同时,只要'./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
的变量,这实际上意味着“此应用程序中的任何位置都不使用此导入的模块”。因此,如果'npm-package/style.css'
具有npm-package
标志,Webpack只会丢弃捆绑中的sideEffects: false
文件,作为“树摇动”过程的一部分。因此,不要写sideEffects: false
总是写"sideEffects": ["*.css"]
。即使你的npm软件包没有导出任何CSS文件,它也可能会在将来发布,这样可以防止上述“CSS文件不包含”错误。