typescript - 将应用程序的一部分分解到它自己的 NPM 包中会导致更大的整体应用程序大小
问题描述
我们在所有项目中都使用create-react-app
Typescript,并且出现了一个稍大的常用 React 组件模块。试图将这些因素分解到它自己的 NPM 包中(为了更容易维护和更好地重用),这里PackageA
称为TestApp
代码存在于同一应用程序的代码库中)。TestApp
是一个非常简陋的应用程序,它基本上只是展示了 中组件的一些(但不是全部)部分PackageA
,以前是项目本身内部的组件,现在删除了这部分,而是从私人发布PackageA
的 .
PackageA
在从组件中分解出来之前的初始 JS 块的大小TestApp
(假设“main”是来自项目本身的代码,而另一个块被认为包含依赖项):
- Gzipped:“主”块~ 31 kB,具有外部依赖项的块~ 193 kB(来自构建应用程序后的输出)
- 解压缩:“主”块~ 93 kB,具有外部依赖项的块~ 653 kB(来自浏览器)
分解后PackageA
的尺寸TestApp
:
- Gzipped:主块〜22 kB,具有外部依赖关系的块〜215 kB(来自构建应用程序后的输出)
- 解压缩:主块~ 57 kB,具有外部依赖项的块~ 745 kB (来自浏览器)
可以看出,整体大小随着13 kB gzip 和56 kB unzip 的增加而增加。这对应于gzipped增加了~ 6%和 unzipped 增加了~ 8%。这并不算多,但我仍然希望它们有点相似。
更多的信息
- 的内容
PackageA
以 es6 模块的形式发布,以允许 tree-shaking,这似乎可以正常工作,因为未使用的部分PackageA
不会发送到TestApp
. PackageA
UglifyJS 在发布之前使用--compress
和--mangle
选项进行缩小。- 源地图不包含在源文件中,
PackageA
但只能单独使用。 dependencies
在PackageA
's下列出的唯一软件包未在package.json
其他任何地方使用,随后在添加到's的同时TestApp
从TestApp
's中删除。使用了相同的版本。的所有其他依赖项都列在 下。package.json
PackageA
package.json
PackageA
peerDependencies
- 在删除
node_modules
文件夹并运行全新安装依赖项后验证所有大小。 - 的内容与
PackageA
从中删除的文件夹完全相同TestApp
,只是添加了一个index.ts
导出其他文件内容的页面。此文件解压缩后的大小< 3 kB 。
这种规模增加的可能来源是什么?我们可以从哪里开始寻找?也许这种增加可能在于代码在包中的转译create-react-app
方式,并且对于包含在项目本身中的代码比对导入的代码更有效地做到这一点。我知道这是一个棘手的问题,可能有很多答案,而且很难回答。
这是tsconfig.json
用于分解的PackageA
:
{
"compilerOptions": {
"target": "es6",
"lib": ["es6", "dom"],
"jsx": "react-jsx",
"module": "es6",
"rootDir": "./src",
"moduleResolution": "node",
"declaration": true,
"sourceMap": true,
"outDir": "./build/esm",
"inlineSources": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
},
"include": ["./src"],
"exclude": ["**/*.test.tsx", "**/*.test.ts", "**/*.stories.tsx"]
}
解决方案
我知道这个问题可能在很大程度上取决于你所处的情况,但我仍然认为我会分享我的过程。
测量尺寸
通常在您构建时,您的框架会输出构建工件的大小。在 的情况下create-react-app
,这些大小似乎是 gzip 压缩版本的大小。
您还可以检查您的浏览器开发工具并注意下载的包的大小,这些包通常不是压缩后的大小,而是实际大小。
在分解某些东西或进行某种比较时,请注意更改前后的不同尺寸。给定关于这些大小应该如何改变的某种直觉,评估是否是这种情况。
分析捆绑包
您可以使用 NextJS @next/bundle-analyzer
,在大多数其他情况下,您可以使用source-map-explorer
. 后者使用源映射将捆绑的代码映射到源;如果源本身有源映射,则使用这些源,否则您将看到对node_modules
. 使用source-map-explorer
,可能值得同时研究视觉表示和 json 表示,因为在很多来源的情况下,视觉表示可能会抑制 json 表示不会参与的某个组件或库的参与。
您可以在source-map-explorer
应用程序的不同版本之间进行一些有价值的比较。
分析源代码到生产代码的特殊情况
例如,在source-map-explorer
确定某些源代码在某些上下文中扩展得更多之后,您可能希望进一步研究该问题。使用源映射(在开发模式下或在生产模式下,如果您有它们可用),您可能想要跟踪输出包中源代码的某个部分的印记。许多浏览器支持将捆绑代码映射到源代码(给定源映射),但并非所有浏览器都允许您做相反的事情。然而,Firefox 具有此功能,它允许您在给定捆绑代码的情况下查看源代码,反之亦然。当您尝试跟踪某些源代码在输出中实际产生的结果时,这非常有用。
我的用例呢?
好吧,以下是在我的情况下导致大小增加的原因列表:
我从包中的索引导入,索引导入包的所有导出。这应该不是 esm 模块的问题,但是如果没有
sideEffects: false
标志,package.json
就会有一些歧义。即使 MY 包中未使用的源没有添加到输出包中,但不能排除一些间接依赖项没有副作用,因此将这些添加到包中,即使导入它们的组件甚至没有使用。添加sideEffects: false
到package.json
我的包中解决了这个问题并减小了包的大小。在我的主项目中保留使用 捆绑的组件时,
create-react-app
由于@babel/plugin-transform-runtime
插件的存在,Babel 在所有转译文件中添加的一些便利方法都被删除了。这个插件删除了这些便利方法,因为在运行这些文件时保证存在一些 Babel 运行时(它们可以从中导入)。当将相同的配置用于已分解的组件时,尺寸缩小得更多。最后,在撰写本文时,
tsc
编译器似乎输出了比 Babel 更详细的代码(请参阅TypeScript:tsc 将 jsx 转换为比 babel 更详细的代码)。在转译我第一次使用的包tsc
和开始使用 Babel 后,尺寸缩小了更多。
总而言之,通过上述更改,输出最终仅比重构前大 1.7 kB(解压缩),这是一个既可以理解又可以接受的结果。
推荐阅读
- java - 如何在关闭活动后立即从活动中删除数据
- puppeteer - Puppeteer-extra 允许 Flash 支持
- c - GetWindowTextLength() 返回 0,可能是因为无效的窗口句柄
- php - 使用ajax插入复选框数组
- python - 大熊猫的双边 t 检验
- javascript - 如何描述一个可以正确序列化的值?
- html - 如何在链接弹簧启动/角度应用程序中获取 th:href="@{}" 而不是 href=""
- c++ - 当分母超出 unsigned long long int 范围时如何使用模?
- c++ - 递归函数的问题(检查一个数字是否是一个完美的平方)
- c# - 使用 ML.Net 为集群 ID 分配标签