模块联合共享服务

问题描述

我正在使用Angular 11和Webpack 5进行新项目。我的工作基于使用Angular CLI的Manfred Steyer的Module Federation Plugin Example回购。 我不知道如何在两个应用程序之间从共享的本地Angular库中共享单例服务。

我会尽力解释我的设置。.非常抱歉这将持续多长时间。

简化的文件结构

root
  package.json
  projects/
    shell/
      src/app/
        app.module.ts
        app.component.ts
      webpack.config.ts <-- partial config
    mfe1/
      src/app/
        app.module.ts
        app.component.ts
      webpack.config.ts <-- partial config
    shared/
      src/lib/
        global.service.ts
      package.json <-- package.json for lib

角度

两个app.component.ts文件都是相同的

import {GlobalService} from 'shared';

@Component({
  selector: 'app-root',templateUrl: './app.component.html'
})
export class AppComponent {
  constructor(shared: SharedService) {}
}

global.service.ts

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class GlobalService {

  constructor() {
    console.log('constructed SharedService');
  }
}

Webpack

shell / webpack.config.ts

const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");

module.exports = {
  output: {
    publicPath: "http://localhost:5000/",uniqueName: "shell"
  },optimization: {
    // Only needed to bypass a temporary bug
    runtimeChunk: false
  },plugins: [
    new ModuleFederationPlugin({
      remotes: {
        'mfe1': "mfe1@http://localhost:3000/remoteEntry.js"
      },shared: ["@angular/core","@angular/common","@angular/router"]
    })
  ],};

mfe1 / webpack.config.ts

const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");

module.exports = {
  output: {
    publicPath: "http://localhost:3000/",uniqueName: "mfe1"
  },plugins: [
    new ModuleFederationPlugin({

      // For remotes (please adjust)
      name: "mfe1",library: {type: "var",name: "mfe1"},filename: "remoteEntry.js",exposes: {
        './Module': './projects/mfe1/src/app/app.module.ts',},};

共享库package.json

{
  "name": "shared","version": "0.0.1","main": "src/public-api.ts","peerDependencies": {
    "@angular/common": "^11.0.0-next.5","@angular/core": "^11.0.0-next.5"
  },"dependencies": {
    "tslib": "^2.0.0"
  }
}

此配置可以编译并运行,但是mfe1实例化一个新的GlobalService。我看到“构造的SharedService”已登录到应用程序加载中,然后在加载远程模块后再次登录。我尝试遵循ScriptedAlchemy的another example,但是我不知道如何使它起作用。它要么编译并运行并创建两个实例,要么无法编译,具体取决于我确定如何弄乱我的配置。

ScriptedAlchemy示例使我似乎需要在shared文件webpack.config.ts库数组中引用共享库。这完全有道理,但我无法正常工作

shell/webpack.config.ts and mfe1/webpack.config.ts
  ...
  shared: ["@angular/core","@angular/router","shared"]

如果以这种方式引用本地库,则不可避免地会在构建过程中出现错误

Error: Module not found: Error: Can't resolve 'shared' in '/Path/to/module-federation-plugin-example/projects/shell/src/app'

我发布的示例已简化。希望不会太过分,但这是link to a repo显示问题

解决方法

TL; DR

  • 确保共享服务的所有模块依赖项也已共享。
  • 确保在webpack.config.ts中正确配置了共享配置

我将注意到我的项目是使用Angular CLI的NX Monorepo。我目前正在使用Angular 11.0.0-next和Webpack 5,在撰写本文时,它们只能作为ng11的可选组件使用。

如果您在tsconfig中使用路径别名,则习惯于导入“ @ common / my-lib”之类的本地库,但是无法在webpack配置中按别名共享模块。此外,如果您使用的是NX,则从绝对或相对库路径导入时,棉绒会抱怨,因此Webpack所需的内容与nx / tslint所需的内容之间是没有联系的。

在我的项目中,我有一些类似以下的库别名

tsconfig.base.json
...
"paths": {
    "@common/facades": [
        "libs/common/facades/src/index.ts"
    ],"@common/data-access": [
        "libs/common/data-access/src/index.ts"
    ]
}

要使其在我的webpack配置中起作用。我们必须使用这些别名,但是要使用import选项告诉webpack在哪里找到库

apps/shell/webpack.config.js
...
plugins: [
    new ModuleFederationPlugin({
        remotes: {
            'dashboard': 'dashboard@http://localhost:8010/remoteEntry.js','reputation': 'reputation@http://localhost:8020/remoteEntry.js'
        },shared:         {
            "@angular/core": {
                "singleton": true
            },"@angular/common": {
                "singleton": true
            },"@angular/router": {
                "singleton": true
            },"@angular/material/icon": {
                "singleton": true
            },"@common/data-access": {
                "singleton": true,"import": "libs/common/data-access/src/index"
            },"@common/facades": {
                "singleton": true,"import": "libs/common/facades/src/index"
            },"rxjs": {
                "singleton": true
            },"ramda": {
                "singleton": true
            }
        }
    })
]

这解决了Webpack在编译时无法找到模块的问题。

第二个问题是我试图共享依赖于公共数据访问服务的公共外观。我不打算共享数据访问服务,因为它们是无状态的,但是这导致我的MFE实例化了新的单例。这必须是因为共享服务具有“未共享”的依赖性。共享我的数据访问层和外观层导致整个应用程序共享单例。

在这里,依赖的问题是最重要的,因为它很难调试。没有错误或任何东西-事情并没有像您期望的那样工作。您甚至可能没有意识到,一开始您有两个共享服务实例。因此,如果遇到此问题,请查看依赖项,并确保您共享所需的一切。这可能是在应用程序之间保持最小共享状态/依赖性的一种论据。

  • 有关@ angular / material / icon共享配置的快速说明。在我的应用程序中,我用每个应用程序需要的图标填充MatIconRegistry。我必须共享@ angular / material / icon才能在每个MFE中共享单例。