Next.js 包大小因动态组件查找而爆炸,如何解决?

IT技术 javascript reactjs webpack next.js
2021-05-16 15:29:28

域名:

检查repo - common.js 包含所有依赖项,即使在相应页面上只使用了一个依赖项。

  • http://localhost:3000/components/ComponentOne
  • http://localhost:3000/components/ComponentTwo

现场演示:点击这里

更多细节:

我有一个应用程序(找到一个非常简化的版本),其中根据用户输入呈现不同的组件。通过组件映射找到要渲染的组件。common.js 包含切换器页面的所有依赖项是有道理的,其中两个组件都必须可访问(以及它们的依赖项)。但是单个页面包含各自的其他依赖项是没有意义的。

总结一下:

  • 我希望能够拥有大量组件,这些组件可以根据用户的输入进行呈现。我的用例对它们进行序列化和反序列化(如此处所示是不可行的,因为组件差异很大并且需要不同的依赖项
  • 我还想将每个组件呈现到它自己的静态生成页面,在那里我从数据库中检索其他 SEO 信息。但是,在这种情况下,我只想为手头的特定组件加载所需的依赖项。

http://localhost:3000

在此处输入图片说明

Selecting ComponentOne results in:

使用 recharts.js 在此处输入图片说明

Selecting ComponentTwo results in:

使用victory.js 在此处输入图片说明

1个回答

问题

TLDR:Next 的 Webpack 配置将动态加载的组件分块作为自己的块,这可能会创建重复或组合的块依赖项。

在您的示例中,我将组件 1 和 2 分别复制为组件 3 和 4。但是,对于组件 4(它是组件 2 的副本),我添加了一个额外的moment-timezone依赖项。结果是一个具有重复victory-pie依赖项的分离块(它还导入了整个库): victorymoment-timezone在此处输入图片说明

解释

即使两个 3rd 方图表包之间存在相当多的依赖共享(主要是两者共享d3依赖),如果组件正在重用恰好具有共享依赖并跨多个路由动态加载的 3rd 方库,Webpack 可能会尝试将这些 3rd 方块组合成一个组合块: 在此处输入图片说明 而不是预期的两个或更多块: 在此处输入图片说明

但是,正如您在上面的块截图中注意到的那样,即使第 3 方包没有在多个路由中重用/重新导入,您仍然有重复的依赖项(例如,大桃子和柠檬绿色块在上面的屏幕截图包含重复的d3-scaled3-timed3-path等依赖块)。

不幸的是,这是通过next/dynamic(也适用于使用 Webpack 的动态import语句)导入的组件的必要和预期行为,因为它必须遍历整个每个动态导入的组件的依赖图并(可能)将它们添加为自己的块——换句话说,在动态加载组件的情况下,Webpack 不知道运行时正在加载什么组件,因此它必须创建一个整个块以便能够根据请求加载(即使其他组件可能共享相同的依赖项,它也不会知道)。不仅如此,因为它不知道动态组件中正在导入/使用什么,所以它不能摇树依赖!因此,当您添加更多动态加载的组件时,这会创建非常大且重复的块。

解决方案

不幸的是,真的没有办法解决。即使我尝试手动将这些依赖项分离和分组为它们自己单独的块(以减少冗余/构建大小),该组件也将不再呈现。这是有道理的,当每个组件以某种方式在主应用程序中成为自己独立的“应用程序”时。

在这种情况下,最简单的解决方案是渲染静态图像而不是动态加载的 React 组件(如视频的缩略图)。

其他想法

我查看了 Next 的 Webpack 配置,并取得了一些进展。您可以创建自己的 webpack splitChunks规则供 Next 使用,这将有助于减少一些块冗余;但是,即便如此,我仍然收到重复的块(主要来自d3共享依赖项)。你可以试试看。绝对不适合胆小的人,因为您将在一个黑洞中追逐一只兔子,并且您将无法实现块分布的完美。也就是说,它确实有助于减少构建尺寸...... 在此处输入图片说明

以下是用作next.config.js文件基础的一些初步工作

下一个.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;
  },
};