动态组件查找导致 Next.js 包大小爆炸,如何解决?
Posted
技术标签:
【中文标题】动态组件查找导致 Next.js 包大小爆炸,如何解决?【英文标题】:Next.js bundle size is exploding as a result of dynamic component lookup, how to solve? 【发布时间】:2021-05-06 22:18:51 【问题描述】:tldr:
检查repo - common.js 包含所有依赖项,即使在相应页面上只使用了一个。
http://localhost:3000/components/ComponentOne http://localhost:3000/components/ComponentTwo现场演示:click here
更多详情:
我有一个应用程序(发现附加了一个非常简化的版本),其中基于用户输入呈现不同的组件。查找要渲染的组件是通过组件映射发生的。 common.js 包含切换器页面的所有依赖项是有道理的,其中两个组件都必须可访问(因此它们的依赖项)。 但是各个页面分别包含其他依赖项是没有意义的。
总结一下:
我希望能够拥有一大组组件,这些组件可以根据用户的输入进行呈现。我的用例对它们进行序列化和反序列化 (as shown here) 是不可行的,因为组件完全不同并且需要不同的依赖项 我还想将每个组件呈现到其自己的静态生成页面,在该页面中我从数据库中检索额外的 SEO 信息。然而,在这种情况下,我只想为手头的特定组件加载所需的依赖项。http://localhost:3000
Selecting ComponentOne results in:
使用 recharts.js
Selecting ComponentTwo results in:
使用victory.js
【问题讨论】:
@RandyCasburn 谢谢!这有点帮助。知道如何在不成为歹徒的情况下实现相同的目标吗?在我的实际应用中,我有数千个组件可以非常动态地插入到聊天中。 再次搜索您的代码库后,我相当肯定我最初的断言是错误的。 @RandyCasburn 是的。我猜想以我的方式定义 componentMap 算是***。 是的。它必须与项目结构有关——我不明白。 @juliomalves Common.js 非常庞大,因为它包含每一个依赖项。 IE。页面 A 还加载页面 B 和所有其他页面的所有依赖项。大约多出 150kb。 【参考方案1】:问题
TLDR:Next 的 Webpack 配置将动态加载的组件分块为自己的块,这可能会创建重复或组合的块依赖项。
在您的示例中,我将组件 1 和 2 分别复制为组件 3 和 4。但是,对于组件 4(它是组件 2 的副本),我添加了一个额外的 moment-timezone
依赖项。结果是一个带有重复的 victory-pie
依赖项的单独块(它还为 victory
和 moment-timezone
包导入了整个库):
解释
即使两个 3rd 方图表包之间存在相当多的依赖共享(主要都是共享 d3
依赖项),如果组件正在重用碰巧具有共享依赖项并在 multiple 路由,Webpack 可能会尝试将这些 3rd 方块组合成一个组合块:
而不是预期的两个或更多块:
但是,正如您将在上面的块屏幕截图中注意到的那样,即使没有跨多个路由重用/重新导入第 3 方包,您仍然具有重复的依赖项(例如,上面屏幕截图中的大桃子和柠檬绿块都包含重复的d3-scale
、d3-time
、d3-path
等依赖块。
不幸的是,这是通过 next/dynamic
导入的组件的必要和预期行为(也适用于使用 Webpack 的动态 import
语句),因为它必须遍历每个组件的整个依赖图动态导入的组件并(可能)将它们添加为自己的块 - 换句话说,在动态加载组件的情况下,Webpack 不知道在运行时正在加载哪个组件,因此它必须为其创建一个完整的块能够根据请求加载(即使其他组件可能共享相同的依赖项,它也不会知道)。不仅如此,由于它不知道在动态组件中导入/使用了什么,它不能摇树依赖!因此,当您添加更多动态加载的组件时,这会创建非常大且重复的块。
解决方案
不幸的是,真的没有解决办法。即使我尝试手动将这些依赖项分离并分组为它们自己的单独块(以减少冗余/构建大小),组件也将不再呈现。这是有道理的,当每个组件在主应用程序中以某种方式被分块成为其自己的独立“应用程序”时。
在这种情况下,最简单的解决方案是渲染静态图像来代替动态加载的 React 组件(如视频的缩略图)。
其他想法
我查看了 Next 的 Webpack 配置并取得了一些进展。您可以创建自己的 webpack splitChunks 规则供 Next 使用,这将有助于减少一些块冗余;但是,即便如此,我仍然得到重复的块(主要来自 d3
共享依赖项)。你可以试试看。绝对不适合胆小的人,因为您将在黑洞中追逐兔子,并且您不会达到完美的块分布。也就是说,它确实有助于减少构建大小......
以下是一些初步工作,可用作您的 next.config.js
文件的基础:
next.config.js
module.exports =
webpack(config, isServer )
/* adds client-side webpack optimization rules for splitting chunks during build-time */
if (!isServer)
config.optimization.splitChunks.cacheGroups =
...config.optimization.splitChunks.cacheGroups,
victory:
test: /[\\/]node_modules[\\/](victory-pie|victory-core|victory-pie\/es)[\\/]/,
name: "victory",
priority: 50,
reuseExistingChunk: true,
,
recharts:
test: /[\\/]node_modules[\\/](recharts|recharts-scale)[\\/]/,
priority: 20,
name: "recharts",
reuseExistingChunk: true,
,
lodash:
test: /[\\/]node_modules[\\/](lodash)[\\/]/,
name: "lodash",
reuseExistingChunk: true,
priority: 40,
,
;
/* return new config to next */
return config;
,
;
【讨论】:
非常感谢。我害怕会是这样的结果。真是太糟糕了,这有点意味着我必须手动创建数千个组件。问题是不同的组件有不同的依赖关系,而且它们很酷的一点是它们是交互的。所以图像也不起作用:/ 更新了答案。有关更多信息,请参阅other thoughts
部分。
非常感谢。将不得不阅读这一点,但如果我理解正确,如果它们不共享共同的依赖关系,我应该能够很好地分割块?手头的示例实际上不是我在生产中面临的示例,因为我只使用一个绘图库。依赖关系彼此相当独立。您能否用一两句话解释一下 cachGroups 是什么?那些单独的 common.js 块?
在为生产构建应用程序时,Webpack 尝试将依赖项分组/拆分为可共享的块。这样,您就拥有更小、更优化的构建尺寸。相反,Webpack 无法自动确定如何分块这些依赖项,因此通过结合使用 Next 的 Webpack 配置和自定义规则,我们可以帮助 Webpack 优化生产构建。查看Webpack documentation 了解更多信息。以上是关于动态组件查找导致 Next.js 包大小爆炸,如何解决?的主要内容,如果未能解决你的问题,请参考以下文章
如何实现具有固定宽度和动态高度的 Next.js Image 组件以保持图像的尺寸?
Next.js:如何将仅外部客户端的 React 组件动态导入到开发的服务器端渲染应用程序中?