Webpack 4 对带有副作用的包的期望是什么:false

What Does Webpack 4 Expect From A Package With sideEffects: false

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

来自 Webpack 4: released today

Over the past 30 days we have worked closely with each of the frameworks to ensure that they are ready to support webpack 4 in their respective cli’s etc. Even popular library’s like lodash-es, RxJS are supporting the sideEffects flag, so by using their latest version you will see instant bundle size decreases out of the box.

来自Webpack docs

The "sideEffects": false flag in big-module's package.json indicates that the package's modules have no side effects (on evaluation) and only expose exports. This allows tools like webpack to optimize re-exports.

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

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

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

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

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

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

  4. 是否有任何其他因素会阻止模块能够使用 sideEffects: false

webpack 团队的 Sean!我会尽我所能代替我们仍在进行中的文档来回答您的问题!

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

每当模块 re-exports 所有导出时,(无论是否使用)它们都需要被评估和执行,以防其中一个导出创建 side-effect 和另一个。

例如,我用照片创建了一个小场景,以更好地形象化案例:

在这张照片中,我们看到三个模块被导入到一个文件中。然后导入的模块 re-exported 来自该模块:

你可以在这里看到 re-exports 的 none 是相互影响的,因此(如果给 webpack 一个信号),我们可以省略 exports bc 甚至被跟踪或使用(大小和构建时间性能优势)。

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

我们选择“sideEffects: false”作为节省编译时间和构建大小的方法,因为这使我们能够立即(明确地)修剪 developers/library 作者知道 side-effect 免费的导出(以 package.json 中的 属性 或多 2-3 行配置为代价)。

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

补充说明:

  1. Obviously side-effects means something particular in fp and would include logging (console or elsewhere) and the throwing of errors. I'm assuming in this context these are perfectly acceptable?

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

  1. Can a module contain circular references and still use sideEffects: false?

理论上应该如此。

  1. Is there any way to verify or that a module is able to use sideEffects: false beyond trying to track down errors caused by its misuse?

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

  1. Are there any other factors that would prevent a module from being able to use sideEffects: false?

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

sideEffects 设置非常模糊,文档中没有充分描述。文档大多像 "there's a sideEffects flag for modules free of any side effects".

共识是"has no sideEffects"短语可以解密为"doesn't talk to things external to the module at the top level"。

我目前的理解是,这个 sideEffects 标志仅适用于 "re-exports",一个 "re-export" 是:

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

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

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

现在,如果 './lib/b.js' 有一些 top-level 行代码做一些 "side effects",即如果 './lib/b.js' 做了类似的事情:

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

然后 './lib/b.js' 会被认为有 "side effects" 因为它的 top-level 代码(在 import './lib/b' 上执行)影响 [=23 范围之外的东西=] 文件.

同时,只要 './lib/b.js' top-level 代码没有到达 *.js 文件之外,那么它就没有任何 "side effects":

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

这些都不是"side effects"。

还有最后一个陷阱:如果 npm 包有任何 *.css 用户可以 import 的文件,那么这些 *.css 文件都是 "side effects",因为:

import 'npm-package/style.css'

没有分配给这个 import 的变量,这实际上意味着 Webpack 的 "this imported module is not used anywhere in the application"。因此,如果 npm-package 具有 sideEffects: false 标志,Webpack 会简单地从包中丢弃 'npm-package/style.css' 文件作为 "tree-shaking" 过程的一部分。所以,不要写 sideEffects: false,而是写 "sideEffects": ["*.css"]。即使您的 npm 包不导出任何 CSS 文件,它也可能在将来导出,这将防止上述 "CSS file not included" 错误。