捆绑时如何减少反应应用程序构建时间和了解 webpack 的行为
How to reduce react app build time and understanding behaviour of webpack when bundling
最近我正在尝试优化网络应用程序 (React) 的性能。假设它有点重,因为它由代码编辑器、Firebase、SQL、AWS SDK 等组成。所以我集成了 react-loadable,它将延迟加载组件,之后,我得到了这个 Javascript 堆内存不足问题。
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory in React
经过一些研究(来自朋友),我开始知道如果我们保持太多延迟加载,webpack 将尝试并行捆绑它们这可能是导致 Javascript 堆内存问题的原因,确认我删除了我的应用程序中的所有延迟加载路由并构建了。现在构建成功了。后来根据社区的建议,我增加了节点堆 space 大小并获得了以下见解
首先我将其增加到 8 GB(8192) 然后构建成功我的构建时间约为 72 分钟,从下一个开始从现在开始,我大约需要 20 分钟。然后我将堆内存大小减小到 4 GB(4096) 并在 15 - 20 分钟 左右获得构建成功。系统配置为 2vCPU, 16GB RAM(AWS EC2 Instance r5a.large
).
接下来,我继续在另一个系统中构建 (Mac book pro, i5, 8 GB RAM, 4 Cores) 现在花了 30分钟,第二次用了20分钟
根据这些数据点,我得到了几个问题
- 我们是否需要在每次添加一些代码时不断增加堆 space?如果是,社区中的平均堆内存大小是多少
- 对于这些类型的重型应用程序,构建系统的通常配置是什么,为什么因为现在我不确定是否增加内核或 RAM 或堆的数量 space 或总的来说与我们的应用代码。
- webpack 是否提供任何类型的解决方案来避免堆内存问题,例如限制并行进程或任何插件?
- 如果它与我们的 App 代码有关,是否有任何标准流程可以调试占用内存的位置并据此进行优化
PS : 有些人建议保留 GENERATE_SOUCREMAP=false
它成功了,但我们需要源映射,因为它们有助于调试生产问题
最后,我可以在不增加堆内存的情况下解决 heap out of memory
问题 space。
如问题中所述,如果我删除所有惰性路由,构建会成功或者我必须保留 4GB 堆 space 才能在大量构建时间下成功。当使用 4GB Heapspace 构建成功时,我观察到将近 8 - 10 个块文件大小接近 1MB。所以我使用 Source map explorer 分析了所有这些块。在所有块中,几乎包含相同的库代码(在我的例子中,它们是 Firebase、视频播放器等,它们很重)
所以在我的假设中,当 webpack 试图捆绑所有这些块时,它必须在每个块中构建所有这些库依赖关系图,这反过来会导致堆内存 space 问题。所以我使用 Loadable components 来延迟加载这些库。
延迟加载所有这些库后,所有块文件的大小几乎减少了一半,并且构建在不增加任何堆的情况下取得成功space,构建时间也减少了。
优化后,如果我继续构建 6vCPU,i7 System 它需要大约 3 - 4 分钟 我观察到基于系统构建时可用的核心数量正在减少。如果我继续在 2vCPU 系统中构建它有时需要大约 20 - 25 分钟
Vanilla webpack 是为整体构建而开发的。它的主要目的是采用许多模块并将它们捆绑成一个(不是很多)。如果你想保持模块化,你想使用 webpack-module-federation
(WMF
):
WMF
允许您开发可以轻松相互依赖(和延迟加载)的独立包。
- 这些包将自动共享彼此之间的依赖关系。
没有 WMF,webpack 允许 none 以上。
简短示例
- 一个库包
app2
提供了一个组件Button
- 一个应用程序包
app1
使用它。
- 到时候,
app1
使用动态请求组件 import
。
- 您可以使用 React.lazy 包装负载,如下所示:
const RemoteButton = React.lazy(() => import("app2/Button"));
- 例如,您可以在
useEffect
或 Route.render
回调等中执行此操作。
app1
可以使用该组件,一旦它被加载。加载时,您可能希望显示加载屏幕(例如使用 Suspense
):
<React.Suspense fallback={<LoadingScreen />}>
<RemoteButton />
</React.Suspense>
- 或者,不使用
lazy
和 Suspense
,只需采用从 import(...)
语句返回的承诺并以您喜欢的任何方式处理异步加载。当然,WMF
并不局限于react
,可以动态加载任何模块。
另一方面,WMF
动态加载必须使用动态import
(即import(...)
),因为:
- 非动态导入将始终在加载时解析(因此使其成为非动态依赖项),并且
- "dynamic
require
" 不能被 webpack 打包,因为浏览器没有 commonjs
的概念(除非你使用一些 hack,在这种情况下,你会丢失相关的 "loading promise
").
文档
尽管根据我的经验,WMF
成熟、易于使用,并且可能已准备好投入生产,it's official documentation is currently only a not all too polished collection of conceptual notes. That is why I would recommend 。
最近我正在尝试优化网络应用程序 (React) 的性能。假设它有点重,因为它由代码编辑器、Firebase、SQL、AWS SDK 等组成。所以我集成了 react-loadable,它将延迟加载组件,之后,我得到了这个 Javascript 堆内存不足问题。
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory in React
经过一些研究(来自朋友),我开始知道如果我们保持太多延迟加载,webpack 将尝试并行捆绑它们这可能是导致 Javascript 堆内存问题的原因,确认我删除了我的应用程序中的所有延迟加载路由并构建了。现在构建成功了。后来根据社区的建议,我增加了节点堆 space 大小并获得了以下见解
首先我将其增加到 8 GB(8192) 然后构建成功我的构建时间约为 72 分钟,从下一个开始从现在开始,我大约需要 20 分钟。然后我将堆内存大小减小到 4 GB(4096) 并在 15 - 20 分钟 左右获得构建成功。系统配置为 2vCPU, 16GB RAM(AWS EC2 Instance r5a.large
).
接下来,我继续在另一个系统中构建 (Mac book pro, i5, 8 GB RAM, 4 Cores) 现在花了 30分钟,第二次用了20分钟
根据这些数据点,我得到了几个问题
- 我们是否需要在每次添加一些代码时不断增加堆 space?如果是,社区中的平均堆内存大小是多少
- 对于这些类型的重型应用程序,构建系统的通常配置是什么,为什么因为现在我不确定是否增加内核或 RAM 或堆的数量 space 或总的来说与我们的应用代码。
- webpack 是否提供任何类型的解决方案来避免堆内存问题,例如限制并行进程或任何插件?
- 如果它与我们的 App 代码有关,是否有任何标准流程可以调试占用内存的位置并据此进行优化
PS : 有些人建议保留 GENERATE_SOUCREMAP=false
它成功了,但我们需要源映射,因为它们有助于调试生产问题
最后,我可以在不增加堆内存的情况下解决 heap out of memory
问题 space。
如问题中所述,如果我删除所有惰性路由,构建会成功或者我必须保留 4GB 堆 space 才能在大量构建时间下成功。当使用 4GB Heapspace 构建成功时,我观察到将近 8 - 10 个块文件大小接近 1MB。所以我使用 Source map explorer 分析了所有这些块。在所有块中,几乎包含相同的库代码(在我的例子中,它们是 Firebase、视频播放器等,它们很重)
所以在我的假设中,当 webpack 试图捆绑所有这些块时,它必须在每个块中构建所有这些库依赖关系图,这反过来会导致堆内存 space 问题。所以我使用 Loadable components 来延迟加载这些库。
延迟加载所有这些库后,所有块文件的大小几乎减少了一半,并且构建在不增加任何堆的情况下取得成功space,构建时间也减少了。
优化后,如果我继续构建 6vCPU,i7 System 它需要大约 3 - 4 分钟 我观察到基于系统构建时可用的核心数量正在减少。如果我继续在 2vCPU 系统中构建它有时需要大约 20 - 25 分钟
Vanilla webpack 是为整体构建而开发的。它的主要目的是采用许多模块并将它们捆绑成一个(不是很多)。如果你想保持模块化,你想使用 webpack-module-federation
(WMF
):
WMF
允许您开发可以轻松相互依赖(和延迟加载)的独立包。- 这些包将自动共享彼此之间的依赖关系。
没有 WMF,webpack 允许 none 以上。
简短示例
- 一个库包
app2
提供了一个组件Button
- 一个应用程序包
app1
使用它。 - 到时候,
app1
使用动态请求组件import
。 - 您可以使用 React.lazy 包装负载,如下所示:
const RemoteButton = React.lazy(() => import("app2/Button"));
- 例如,您可以在
useEffect
或Route.render
回调等中执行此操作。
- 例如,您可以在
app1
可以使用该组件,一旦它被加载。加载时,您可能希望显示加载屏幕(例如使用Suspense
):<React.Suspense fallback={<LoadingScreen />}> <RemoteButton /> </React.Suspense>
- 或者,不使用
lazy
和Suspense
,只需采用从import(...)
语句返回的承诺并以您喜欢的任何方式处理异步加载。当然,WMF
并不局限于react
,可以动态加载任何模块。
另一方面,WMF
动态加载必须使用动态import
(即import(...)
),因为:
- 非动态导入将始终在加载时解析(因此使其成为非动态依赖项),并且
- "dynamic
require
" 不能被 webpack 打包,因为浏览器没有commonjs
的概念(除非你使用一些 hack,在这种情况下,你会丢失相关的 "loadingpromise
").
文档
尽管根据我的经验,WMF
成熟、易于使用,并且可能已准备好投入生产,it's official documentation is currently only a not all too polished collection of conceptual notes. That is why I would recommend