“React并未被使用,但仍然被引入到捆绑包中”。

huangapple go评论61阅读模式
英文:

React is not being used, yet is still pulled in into a bundle

问题

以下是您要的翻译:

tldr; 什么阻止了在我的项目中消除 React 的死代码?
它没有被使用,除了在我所使用的库的一个未使用的工具中。

我最近创建了一个用于重试失败的动态导入的小库。我创建了一个与 React 无关的模块,另一个专门处理了 React 的特殊情况。这是为了确保消费者在不使用 React 工具时不会引入 React。然后,我创建了一个索引模块,它引入了这两个模块并导出了命名常量。

import _createDynamicImportWithRetry from "./dynamic-import-with-retry";
import _reactLazy from "./react-lazy";

export const reactLazyWithRetry = _reactLazy;
export const createDynamicImportWithRetry = _createDynamicImportWithRetry;

这是 package.json 中的 main 字段。

我之前的阅读中认为,当 Vite 使用这个库时,它应该能够了解只导入命名导出 createDynamicImportWithRetry 只需要引入 "./dynamic-import-with-retry",而不需要引入其他导入。不幸的是,事实似乎并非如此:我发布了一个项目,显示情况并非如此:无论是否使用它,都会捆绑传递性的 React 依赖。

我本来以为在生产捆绑期间进行死代码分析将修剪掉树中未使用的导入。如何确保死代码分支的修剪发生?

复现:

mkdir tmp-proj && cd tmp-proj && mkdir src

echo '<script type="module" src="./src/index.js"></script>' > index.html

echo "import * as o from '@fatso83/retry-dynamic-import'
o.reactLazyWithRetry( () => Promise.resolve())" > src/index.js

npm i @fatso83/retry-dynamic-import; npm i vite

npx vite build 

vite v4.3.5 building for production...
✓ 13 modules transformed.
dist/index.html                0.08 kB │ gzip: 0.09 kB
dist/assets/index-4e07d3ed.js  9.27 kB │ gzip: 3.89 kB

这里的问题是那 9KB。它应该更接近 500 字节,但它引入了 React 的全部生产构建 :/

英文:

tldr; what is preventing React from being dead-code eliminated in my project?
It is not being used, apart in an unused utility in a library I am consuming.


I recently created a little library for retrying failing dynamic imports. I made a non-react-dependant module and another one that just handled the react specifics. This was to ensure a consumer would not pull in React if not using the react utils. I then created an index module that pulled the two in and exported named constants

import _createDynamicImportWithRetry from &quot;./dynamic-import-with-retry&quot;;
import _reactLazy from &quot;./react-lazy&quot;;

export const reactLazyWithRetry = _reactLazy;
export const createDynamicImportWithRetry = _createDynamicImportWithRetry;

This is the main field in package.json.

I assumed from previous reading that when Vite consumes this library it should be able to gather that just importing the named export createDynamicImportWithRetry would only require pulling in "./dynamic-import-with-retry", not the other import. Unfortunately, that does not seem to be the case: I published a project that shows it not to be the case: the transitive React dependency is being bundled, no matter if it is unused.

I would have assumed that dead-code analysis during bundling for production would have pruned the unused imports of the tree. How can I ensure pruning of dead branches take place?

Reproduction:

mkdir tmp-proj &amp;&amp; cd tmp-proj &amp;&amp; mkdir src

echo &#39;&lt;script type=&quot;module&quot; src=&quot;./src/index.js&quot;&gt;&lt;/script&gt;&#39; &gt; index.html

echo &quot;import * as o from &#39;@fatso83/retry-dynamic-import&#39;
o.reactLazyWithRetry( () =&gt; Promise.resolve())&quot; &gt; src/index.js

npm i @fatso83/retry-dynamic-import; npm i vite

npx vite build 

vite v4.3.5 building for production...
✓ 13 modules transformed.
dist/index.html                0.08 kB │ gzip: 0.09 kB
dist/assets/index-4e07d3ed.js  9.27 kB │ gzip: 3.89 kB

The 9KB there is the problem. It should be closer to 500 bytes, but it pulls in all of the React production build :/

答案1

得分: 2

你需要告诉你正在使用的打包工具,React 应该由你库的客户端提供,而不应该与你的库一起打包。这被称为外部化

要了解如何在 package.json 和你的打包工具中完成这个操作的一般思路,你可以查看我的之前的回答,使用 Rollup。如果你正在使用 Vite,外部化配置更加简单,可以参考这里

英文:

You've got to tell whichever bundler you are using that react is supposed to be provided by the client of your lib, ie. that it shouldn't be bundled with your lib. This is called externalization.

To get a general idea how this is done in both package.json and in your bundler, you can check my previous answer using Rollup. If you are using Vite, the externalization is even simpler to configure.

答案2

得分: 1

我在Vite讨论板上发布了这个问题,结果发现Rollup目前还不能跟踪赋值操作(截止到2023年5月)。

> Rollup目前还不能跟踪赋值操作,因此在赋值操作的右侧,所有属性都会被取消优化。另外,Rollup取消优化不了解执行流程,因此它假设lazy可能是require$$0.lazy=()=>{}中的一个setter,这是一个副作用。可能不太容易修复。

由于Vite依赖于Rollup进行捆绑,除了将导出移出根模块并在package.json中单独导出它(这是我在版本2中所做的),没有其他办法。

英文:

I posted this on the Vite discussion board and it turns out that Rollup cannot track assignments yet (per May 2023).

> Rollup cannot track assignments (yet), so we deoptimize all properties of the right-hand side on assignments. Also, Rollup deoptimization does not know the execution flow, so it assumes that lazy might be a setter in require$$0.lazy=()=>{}, and that is the side effect. Probably not too easy to fix.

Since Vite relies on Rollup for bundling, there is nothing to do, apart from moving the export out of the root module and exporting it as its own specific export in package.json (which is what I did for version 2).

huangapple
  • 本文由 发表于 2023年5月14日 00:18:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/76243750.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定