模块联邦实践之依赖分离

127 阅读3分钟

在前端开发中,性能优化是一个永恒的话题。随着应用变得越来越复杂,开发者不断寻找新的方法来提高加载速度和用户体验。传统上,我们可能会使用Webpack的Externals功能来引入CDN资源,以减少打包体积和加快加载速度。然而,随着Webpack 5引入的模块联邦(Module Federation)功能,我们现在有了一个更强大的工具来优化我们的应用性能。

为什么不是Externals

Externals功能允许开发者从最终的bundle中排除某些依赖,这些依赖通常会被托管在CDN上,通过全局变量的形式在运行时提供。虽然这种方法可以减少应用的打包体积,但需要手动管理外部依赖,不适合复杂的模块共享需求。 兼容项目中引入了第三方资源库A,A又依赖了B,C,D 。Externals 并不能自动处理这些依赖关系。

使用模块联邦加载公共资源库

模块联邦是Webpack 5的一个新特性,它允许一个应用动态地从另一个应用加载代码,就像从本地模块导入一样。这为性能优化提供了新的可能性:

动态加载:模块联邦支持动态加载远程模块,这意味着你可以根据需要懒加载模块,而不是在应用启动时加载所有模块。

独立部署:每个远程模块可以独立部署和更新,不需要重新部署整个应用。

共享模块:不同的应用可以共享模块,而不需要将其打包进每个应用的bundle中。

要使用模块联邦加载公共资源库,需要在资源库的Webpack配置中使用ModuleFederationPlugin来定义要暴露的模块:

// webpack.config.js
const {ModuleFederationPlugin} = require("webpack").container;

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'libraryName',
      filename: 'remoteEntry.js',
      exposes: {
        './Component': './src/Component',
      },
      // 其他配置...
    }),
  ],
  // 其他配置...
};

然后,在消费者应用的Webpack配置中,你需要添加一个远程模块的引用:

// webpack.config.js
const {ModuleFederationPlugin} = require("webpack").container;

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      remotes: {
        libraryName: 'libraryName@https://cdn.example.com/remoteEntry.js',
      },
      // 其他配置...
    }),
  ],
  // 其他配置...
};

完整示例代码可以看这里: stackblitz.com/github/webp…

这种方法不仅减少了应用的初始加载时间,而且还提供了更好的缓存优势,因为公共库可以被多个应用共享,而不是被重复下载。需要注意的是,这种方式可能会引入不同版本的同一依赖。比如 项目中使用了 @ant-design/flowchart(依赖 @antv/x6) ,但是因为功能扩展的原因项目中又单独使用了@antv/x6 ,这种情况下,需要在host和remote应用的的package.json 中加上 @antv/x6 ,并确保和@ant-design/flowchart 中的主版本一致。实际的使用中,可以通过痛过Webpack插件的形式给出版本本地和远端公共库版本对比信息,并依据项目发布情况调整公共库APP的缓存时间。