Webpack 4对包含sideEffects的包的期望是什么:false

时间:2018-03-07 20:35:42

标签: javascript webpack bundler package.json side-effects

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

来自Webpack 4: released today

  

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

来自Webpack docs

  

“sideEffects”:big-module的package.json中的false标志表示包的模块没有副作用(评估时),只暴露出口。这允许像webpack这样的工具优化再出口。

虽然第二个链接显示了使用该标志的结果,但它没有清楚地解释什么构成副作用。 ES6包括概述here模块的副作用概念,但这与Webpack考虑副作用有何关联。

sideEffects标志的上下文中,模块需要避免使用sideEffects:false而没有问题,或者说对话,模块需要做什么才能使用sideEffects:false没有问题。

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

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

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

  3. 是否有任何方法可以验证或模块是否能够验证模块是否sideEffects: false除了尝试追踪因误用而导致的错误?

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

2 个答案:

答案 0 :(得分:44)

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

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

每当模块重新导出时,如果其中一个导出与另一个导出产生副作用,则需要评估并执行所有导出(无论是否使用)。

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

在这张照片中,我们看到具有单个导入的3个单独模块被导入单个模块,然后获取这些默认导出,从该模块重新导出

Example of No Side Effects from Reexported Modules

你可以在这里看到,没有一个reexport会受到彼此影响,因此(如果给webpack一个信号),我们可以省略导出bc甚至被跟踪或使用(大小)并建立时间性能优势)。

enter image description here

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

我们选择“sideEffects:false”作为节省编译时间和构建大小的方法,因为这允许我们立即修剪(显式)导出开发人员/库作者知道副作用免费的导出(代价是package.json中的属性,或2-3行的配置)。

虽然从技术上讲这个例子非常原始,但是当你开始处理Frameworks或Libraries,它们为开发者体验(Three.js,Angular,lodash-es等)重新出口一大堆模块时,性能提升当(如果它们是sideEffect free模块导出)你以这种方式标记它们时是很重要的。

其他说明:

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

在这是试图解决的情况下,是的。只要对模块出口产生的影响不受其他会导致修剪不可接受的影响。

  
      
  1. 模块是否可以包含循环引用,仍然使用sideEffects: false?
  2.   

理论上它应该。

  
      
  1. 是否有任何方法可以验证或模块是否能够使用sideEffects: false,而不是尝试追踪因滥用而导致的错误?
  2.   

不是我所知道的,但这将是一个很棒的工具。

  
      
  1. 是否有任何其他因素会妨碍模块使用sideEffects: false
  2.   

如果该属性不在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文件不包含”错误。