带有 es6 模块的 NPM 包的最佳实践——是否捆绑

Best practice for NPM package with es6 modules - bundle or not

编写一个包含 es6 模块的 NPM 包,最好的做法是将源文件分开

package.json
esm
 index.js
 Content1
  Content1A.js
  Content1A.js.map
  Content1B.js
  Content1B.js.map
 Content2
  Content2A.js
  Content2A.js.map
  Content2B.js
  Content2B.js.map

index.js 引用子文件夹中的内容,还是将其捆绑到一个文件中更好

package.json
esm
 contents.js
 contents.js.map

似乎第一种方法对 CommonJS 模块有优势,因为它为消费者提供了直接从源代码导入的可能性,从而跳过来自 index.js 的未使用的导入(因为 CommonJS 模块不可摇树)但是es6 模块,这个参数消失了。

不同的捆绑器可能具有不同的功能。这个答案的其余部分指的是 Webpack,它是最常见的捆绑器之一,应该会影响这方面的决策。

决定是否捆绑库的最重要因素应该与 tree-shaking 有关。我没有想到其他重要方面。

Webpack 中影响 tree-shaking 的参数

  1. sideEffects: false

package.json中设置,表示包中的模块是否有副作用,需要在模块被导入但不被消费时执行。将其设置为 false 表示没有模块有副作用。也可以设置为具有副作用和其他更复杂值的模块列表。默认似乎是 true 表示所有模块都有副作用。

在您的包中使用入口点索引时,此参数起着重要作用,所有包导出都从中重新导出。如果此设置不正确,从该索引中进行的稀疏导入很容易导致您的整个包被捆绑。

  1. optimization.usedExports: true

webpack.config.js 中的设置指示 Webpack 可以排除所有未使用的导出。这会激活 Terser 用来删除模块内未使用代码的启发式方法。默认设置为 true

在玩具场景中,此设置似乎足够有效,而 sideEffects 标志似乎并没有发挥多大作用。在具有更复杂代码的真实场景中情况并非如此,在这种情况下,这种启发式方法更难做好。

  1. /*#__PURE__*/

要在语句(例如函数)之前使用的注释,以指示如果未明确使用它们可以将其排除在外。这些注释还在 Terser 用于删除模块内未使用代码的启发式方法中发挥了作用。

结论

为了让您的消费者从 tree-shaking 中获得最大收益,似乎建议 不要 捆绑您的 es6 npm 包,而是让单独的输入模块保持独立,以便sideEffects package.json 中的设置可能会导致消费者捆绑器尽可能多地修剪未使用的模块。依赖模块内部的 optimization.usedExports,评估包内容并在您认为可能产生重大影响的地方添加 /*#__PURE__*/ 注释。如果所有东西都捆绑在同一个文件中,package.json 中的 sideEffects 标志就不能完成工作的主要部分,因为所有东西都在同一个模块中,随后我们必须依赖很多额外的/*#__PURE__*/ consumer bundler 中的注解和启发式方法使 tree-shaking 尽可能高效,这需要你做更多(在注解方面)并且没有任何特别的优势。请记住在生产模式下构建您的包,否则优化并不总是有效。

来源