# 使用模块联邦扩展 Angular 应用程序:一步步指南 欢迎使用我们系列的第二部分,本系列讲述了如何利用模块联邦(MF)增强 Angular 应用程序。在本指南中,我们将深入探讨如何将 MF 无缝地整合到现有的普通 Angular 应用程序中。 ## 理解我们的工具:@angular-architects/module-federation 值得注意的是,Angular CLI 默认并不支持加载自定义 Webpack 配置。为了解决这个问题,我们的方法涉及使用一个专门的自定义构建器更新 Angular CLI 构建器。这种修改允许扩展 Webpack 配置文件,对于集成像模块联邦这样的高级特性至关重要。 当本指南专注于使用 `@angular-architects/module-federation` 库提供的自定义构建器时,你可以根据自己的专业能力用另一种构建器替换它。对于那些对如何实现 Angular CLI 构建器内自定义 Webpack 配置感兴趣的人,我们建议阅读 DigitalOcean 的文章 [如何使用 Angular CLI 构建器使用自定义 webpack 配置](https://www.digitalocean.com/community/tutorials/angular-custom-webpack-config)。这个资源提供了宝贵的见解和实际步骤,用于自定义你的 Angular 构建过程,超越标准配置。 ### 库功能 - **更新构建配置**:它允许扩展 Webpack 配置文件。 - **应用服务**:便于单独服务应用程序,不管是通过标准的浏览器交互还是作为微前端。 ## 设置主应用程序 ### 使用 Schematics 初始化 在项目的根目录下运行以下命令: ```bash ng add @angular-architects/module-federation --project app-micro --type dynamic-host --port 4200 ``` 这个命令为你的主项目配置模块联邦,更新 Angular CLI 配置,创建一个用于加载远程入口的清单文件,并重新组织启动引导过程。 ### 处理模式冲突 一些 Angular 模式,比如 `@angular/pwa` 和 `@angular/material`,预期在 `main.ts` 中进行启动引导。如果你计划使用这些模式,临时切换启动引导过程: ```bash ng g @angular-architects/module-federation:boot-async false --project [YOUR_MAIN_PROJECT] ng add [YOUR_LIBRARIES_OF_CHOICE] --project yourProject ng g @angular-architects/module-federation:boot-async true --project [YOUR_MAIN_PROJECT] ``` ### 更新清单文件 在实施模式变更后,修改清单文件(`mf.manifest.json`)和应用程序路由配置(`app-routing.module.ts`)是确保你的 Angular 应用程序正常运行的关键。 `mf.manifest.json` 中的默认配置被设置为在端口 `4200` 上提供文件服务。然而,这个端口通常用于主应用程序。 为了避免冲突,请更新用于服务次要应用程序的端口。例如,将端口更改为 `4201`。你的 `mf.manifest.json` 文件应该反映出这个更改,如下所示: ```json { "login": "http://localhost:4201/remoteEntry.js" } ``` 通过更新这些配置,你为在不同端口上运行主应用程序和次要 Angular 应用程序建立了一个明确且功能性的结构,从而促进了它们的独立操作和集成。 - **简化示例:** 在本指南中,为了简单起见,清单配置是静态定义在 JSON 文件中的。 - **生产中的动态配置:** 对于真实应用程序,建议动态地提供清单配置。这可以通过以下方式实现: - 使用生产版本替换静态 JSON 文件。 - 在服务器上设置一个端点,返回清单配置。 :::details NOTE 当选择动态服务时,更新 `main.ts` 文件以修改 `initFederation` 函数用来检索清单数据的 URL。请注意,在这种配置下,如果没有互联网连接,应用程序将无法加载。然而,在使用静态配置的情况下,你可以处理连接错误,特别是如果你的应用程序已经被缓存过了。 ::: 4. **集成清单到路由**: _修改清单文件:_ - 如前所述,如有必要,请在 `mf.manifest.json` 文件中调整端口。 _更新应用程序路由 (`app-routing.module.ts`):_ - 移除现有的导入代码(例如 `import('@@login')`)。 - 替换为 `loadRemoteModule` 函数的调用(从 `@angular-architects/module-federation` 模块中导入)。 - 使用以下对象配置 `loadRemoteModule` 函数: ```json { "type": "manifest", "remoteName": "login", "exposedModule": "./Module", } ``` - `type`: Denotes the remote configuration type (`'manifest'` in this case). - `remoteName`: The module's name as declared in the manifest. - `exposedModule`: Path of the module in the secondary app. The final result should look similar to the following example: ```typescript const routes: Routes = [ ... { path: '', canMatch: [isNotLogged], loadChildren: () => loadRemoteModule({ type: 'manifest', remoteName: 'login', exposedModule: './Module' }).then( (m) => m.LoginModule, ), }, ... ]; ``` 已经遵循这些步骤后,主应用程序现在已配置为远程加载其他应用程序。下一个阶段涉及设置次级应用程序,以确保它们可以以这种方式被加载。 ## 设置远程应用程序 在配置次级应用程序时,类似于主应用程序,将更新几个项目组件。值得注意的是,这个过程不会创建清单文件,但会修改 `webpack.config.js` 文件,在其中添加比通常在主应用程序配置中找到的更多细节。 ```bash ng add @angular-architects/module-federation --project login --type remote --port 4201 ``` :::tip 我们要使用 `--type remote` 而不是 `--type dynamic-host`。 ::: ### 理解 'exposes' - **"exposes" 的作用:** `webpack.config.js` 文件中的 `exposes` 属性扮演着关键角色。它包含一个对象配置,其中每个键值对类似于一个路由。这个属性决定了要暴露给加载器的模块。 - **链接到 "exposedModule":** 这与我们之前的步骤相关,在路由配置中使用了 `exposedModule` 属性,表明了要加载的特定模块路由。 ### Webpack 配置中的调整 _识别配置不匹配:_ 在当前设置中,路由配置尝试加载一个名为 `./Module` 的模块,但这在现有的 Webpack 配置中并未指定。 为了纠正这个问题,修改 `webpack.config.js` 文件: - 移除现有的 `./Component` 键。 - 添加一个新的 `./Module` 键,确保它指向加载应用程序模块的正确路径。例如: ```javascript exposes: { './Module': './projects/login/src/app/feature/login/login.module.ts', }, ``` 通过这次更新,次级应用程序已经被正确配置,以便暴露所需的模块,与主应用程序中设定的远程加载需求相匹配。 ## 测试和运行应用程序 - **运行主应用程序**:`ng serve` - **运行次级应用程序**:`ng serve login` - 在启动了两个应用程序之后,刷新主应用程序以查看集成的实际操作。次级应用程序现在应该能够在主应用程序中正确加载。 - 此外,通过在浏览器中导航到 `localhost:4201`,你可以观察到登录应用程序作为一个独立的实体在运行。 ## 构建配置的优化 虽然我们目前使用了 `@angular-architects/module-federation/webpack` 包中的 `shareAll`,一个更加精细的方法是使用 `share` 函数共享仅必要的依赖。这种选择性的共享优化了应用程序的性能和资源利用率。