开发者问题收集

如何使用具有模块联合和 NX 的通用 shell 创建基于不同技术的微前端?

2022-12-05
1379

我正在尝试使用 React 和 Angular(远程)创建微前端,这些微前端在基于 React(主机/shell)的 shell 应用程序中使用,使用 Webpacks Module Federation

因此我使用了 NX 提供的官方文档 --> 具有动态模块联合的高级 Angular 微前端

这是我所做的:

1. npx create-nx-workspace pace-microfrontends
2. npm install @nrwl/react --save-dev
3. npm install @nrwl/angular --save-dev
4. npx nx g @nrwl/react:host shell
5. npx nx g @nrwl/angular:remote angular-microfrontend --host=shell
6. npx nx g @nrwl/react:remote react-microfrontend --host=shell

因此,我创建了一个新的 NX 工作区,添加了缺少的依赖项,基于 React 生成了 shell/主机应用程序,并添加了两个微前端(一个基于 React,一个基于 Angular),它们应该托管在 shell 中。

之后,需要在 shell 中向 Angular 微前端添加缺少的路由,因为它不是由 NX 像为 React 微前端那样生成的,如下所示:

apps/shell/src/app/app.tsx

import * as React from 'react';
import NxWelcome from './nx-welcome';
import { Link, Route, Routes } from 'react-router-dom';

const ReactMicrofrontend = React.lazy(
  () => import('react-microfrontend/Module')
);

const AngularMicrofrontend = React.lazy(
  () => import('angular-microfrontend/Module')
);

export function App() {
  return (
    <React.Suspense fallback={null}>
      <ul>
        <li>
          <Link to="/">Home</Link>
        </li>
        <li>
          <Link to="/react-microfrontend">ReactMicrofrontend</Link>
        </li>
        <li>
          <Link to="/angular-microfrontend">AngularMicrofrontend</Link>
        </li>
      </ul>
      <Routes>
        <Route path="/" element={<NxWelcome title="shell" />} />
        <Route path="/react-microfrontend" element={<ReactMicrofrontend />} />
        <Route
          path="/angular-microfrontend"
          element={<AngularMicrofrontend />}
        />
      </Routes>
    </React.Suspense>
  );
}

export default App;

由于模块联合配置也不正确(它找不到 remoteEntry.js 文件),我还必须将其从此更改为:

apps/shell/module-federation.config.js

// @ts-check

/**
 * @type {import('@nrwl/devkit').ModuleFederationConfig}
 **/
const moduleFederationConfig = {
  name: 'shell',
  remotes: [react-microfrontend'],
};

module.exports = moduleFederationConfig;

..更改为:

apps/shell/module-federation.config.js

// @ts-check

/**
 * @type {import('@nrwl/devkit').ModuleFederationConfig}
 **/
const moduleFederationConfig = {
  name: 'shell',
  remotes: [
    ['angular-microfrontend', 'http://localhost:4201/remoteEntry.mjs'],
    ['react-microfrontend', 'http://localhost:4202/remoteEntry.js'],
  ],
};

module.exports = moduleFederationConfig;

经过这些调整后,可以使用 npm start 启动 shell 应用程序。

HomeReactMicrofrontend 的路由工作正常,但只要我单击 AngularMicrofrontend ,就会出现以下错误:

react_devtools_backend.js:4012 Warning: lazy: Expected the result of a dynamic import() call. Instead received: [object Module]

Your code should look like: const MyComponent = lazy(() => import('./MyComponent')) at RenderedRoute (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:4840:5) at Routes (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:5272:5) at Suspense at App at Router (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:5204:15) at BrowserRouter (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:3464:5) overrideMethod @ react_devtools_backend.js:4012 printWarning @ react.development.js:209 error @ react.development.js:183 lazyInitializer @ react.development.js:1400 resolveLazy @ react-dom.development.js:14907 reconcileSingleElement @ react-dom.development.js:15718 reconcileChildFibers @ react-dom.development.js:15808 reconcileChildren @ react-dom.development.js:19174 updateContextProvider @ react-dom.development.js:21154 beginWork @ react-dom.development.js:21649 beginWork$1 @ react-dom.development.js:27426 performUnitOfWork @ react-dom.development.js:26557 workLoopConcurrent @ react-dom.development.js:26543 renderRootConcurrent @ react-dom.development.js:26505 performConcurrentWorkOnRoot @ react-dom.development.js:25738 workLoop @ scheduler.development.js:266 flushWork @ scheduler.development.js:239 performWorkUntilDeadline @ scheduler.development.js:533 queue. @ task.js:61 run @ task.js:35 listener @ task.js:46 react_devtools_backend.js:4012 Warning: lazy: Expected the result of a dynamic import() call. Instead received: [object Module]

Your code should look like: const MyComponent = lazy(() => import('./MyComponent')) at Lazy at RenderedRoute (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:4840:5) at Routes (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:5272:5) at Suspense at App at Router (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:5204:15) at BrowserRouter (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:3464:5) overrideMethod @ react_devtools_backend.js:4012 printWarning @ react.development.js:209 error @ react.development.js:183 lazyInitializer @ react.development.js:1400 mountLazyComponent @ react-dom.development.js:19944 beginWork @ react-dom.development.js:21593 beginWork$1 @ react-dom.development.js:27426 performUnitOfWork @ react-dom.development.js:26557 workLoopConcurrent @ react-dom.development.js:26543 renderRootConcurrent @ react-dom.development.js:26505 performConcurrentWorkOnRoot @ react-dom.development.js:25738 workLoop @ scheduler.development.js:266 flushWork @ scheduler.development.js:239 performWorkUntilDeadline @ scheduler.development.js:533 queue. @ task.js:61 run @ task.js:35 listener @ task.js:46 react_devtools_backend.js:4012 Warning: lazy: Expected the result of a dynamic import() call. Instead received: [object Module]

Your code should look like: const MyComponent = lazy(() => import('./MyComponent')) at Lazy at RenderedRoute (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:4840:5) at Routes (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:5272:5) at Suspense at App at Router (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:5204:15) at BrowserRouter (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:3464:5) overrideMethod @ react_devtools_backend.js:4012 printWarning @ react.development.js:209 error @ react.development.js:183 lazyInitializer @ react.development.js:1400 mountLazyComponent @ react-dom.development.js:19944 beginWork @ react-dom.development.js:21593 callCallback @ react-dom.development.js:4164 invokeGuardedCallbackDev @ react-dom.development.js:4213 invokeGuardedCallback @ react-dom.development.js:4277 beginWork$1 @ react-dom.development.js:27451 performUnitOfWork @ react-dom.development.js:26557 workLoopConcurrent @ react-dom.development.js:26543 renderRootConcurrent @ react-dom.development.js:26505 performConcurrentWorkOnRoot @ react-dom.development.js:25738 workLoop @ scheduler.development.js:266 flushWork @ scheduler.development.js:239 performWorkUntilDeadline @ scheduler.development.js:533 queue. @ task.js:61 run @ task.js:35 listener @ task.js:46 react-dom.development.js:20013 Uncaught Error: Element type is invalid. Received a promise that resolves to: undefined. Lazy element type must resolve to a class or function. at mountLazyComponent (react-dom.development.js:20013:1) at beginWork (react-dom.development.js:21593:1) at HTMLUnknownElement.callCallback (react-dom.development.js:4164:1) at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:1) at invokeGuardedCallback (react-dom.development.js:4277:1) at beginWork$1 (react-dom.development.js:27451:1) at performUnitOfWork (react-dom.development.js:26557:1) at workLoopConcurrent (react-dom.development.js:26543:1) at renderRootConcurrent (react-dom.development.js:26505:1) at performConcurrentWorkOnRoot (react-dom.development.js:25738:1) mountLazyComponent @ react-dom.development.js:20013 beginWork @ react-dom.development.js:21593 callCallback @ react-dom.development.js:4164 invokeGuardedCallbackDev @ react-dom.development.js:4213 invokeGuardedCallback @ react-dom.development.js:4277 beginWork$1 @ react-dom.development.js:27451 performUnitOfWork @ react-dom.development.js:26557 workLoopConcurrent @ react-dom.development.js:26543 renderRootConcurrent @ react-dom.development.js:26505 performConcurrentWorkOnRoot @ react-dom.development.js:25738 workLoop @ scheduler.development.js:266 flushWork @ scheduler.development.js:239 performWorkUntilDeadline @ scheduler.development.js:533 queue. @ task.js:61 run @ task.js:35 listener @ task.js:46 react_devtools_backend.js:4012 Warning: lazy: Expected the result of a dynamic import() call. Instead received: [object Module]

Your code should look like: const MyComponent = lazy(() => import('./MyComponent')) at RenderedRoute (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:4840:5) at Routes (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:5272:5) at Suspense at App at Router (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:5204:15) at BrowserRouter (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:3464:5) overrideMethod @ react_devtools_backend.js:4012 printWarning @ react.development.js:209 error @ react.development.js:183 lazyInitializer @ react.development.js:1400 resolveLazy @ react-dom.development.js:14907 reconcileSingleElement @ react-dom.development.js:15718 reconcileChildFibers @ react-dom.development.js:15808 reconcileChildren @ react-dom.development.js:19174 updateContextProvider @ react-dom.development.js:21154 beginWork @ react-dom.development.js:21649 beginWork$1 @ react-dom.development.js:27426 performUnitOfWork @ react-dom.development.js:26557 workLoopSync @ react-dom.development.js:26466 renderRootSync @ react-dom.development.js:26434 recoverFromConcurrentError @ react-dom.development.js:25850 performConcurrentWorkOnRoot @ react-dom.development.js:25750 workLoop @ scheduler.development.js:266 flushWork @ scheduler.development.js:239 performWorkUntilDeadline @ scheduler.development.js:533 queue. @ task.js:61 run @ task.js:35 listener @ task.js:46 react_devtools_backend.js:4012 Warning: lazy: Expected the result of a dynamic import() call. Instead received: [object Module]

Your code should look like: const MyComponent = lazy(() => import('./MyComponent')) at Lazy at RenderedRoute (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:4840:5) at Routes (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:5272:5) at Suspense at App at Router (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:5204:15) at BrowserRouter (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:3464:5) overrideMethod @ react_devtools_backend.js:4012 printWarning @ react.development.js:209 error @ react.development.js:183 lazyInitializer @ react.development.js:1400 mountLazyComponent @ react-dom.development.js:19944 beginWork @ react-dom.development.js:21593 beginWork$1 @ react-dom.development.js:27426 performUnitOfWork @ react-dom.development.js:26557 workLoopSync @ react-dom.development.js:26466 renderRootSync @ react-dom.development.js:26434 recoverFromConcurrentError @ react-dom.development.js:25850 performConcurrentWorkOnRoot @ react-dom.development.js:25750 workLoop @ scheduler.development.js:266 flushWork @ scheduler.development.js:239 performWorkUntilDeadline @ scheduler.development.js:533 queue. @ task.js:61 run @ task.js:35 listener @ task.js:46 react_devtools_backend.js:4012 Warning: lazy: Expected the result of a dynamic import() call. Instead received: [object Module]

Your code should look like: const MyComponent = lazy(() => import('./MyComponent')) at Lazy at RenderedRoute (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:4840:5) at Routes (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:5272:5) at Suspense at App at Router (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:5204:15) at BrowserRouter (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:3464:5) overrideMethod @ react_devtools_backend.js:4012 printWarning @ react.development.js:209 error @ react.development.js:183 lazyInitializer @ react.development.js:1400 mountLazyComponent @ react-dom.development.js:19944 beginWork @ react-dom.development.js:21593 callCallback @ react-dom.development.js:4164 invokeGuardedCallbackDev @ react-dom.development.js:4213 invokeGuardedCallback @ react-dom.development.js:4277 beginWork$1 @ react-dom.development.js:27451 performUnitOfWork @ react-dom.development.js:26557 workLoopSync @ react-dom.development.js:26466 renderRootSync @ react-dom.development.js:26434 recoverFromConcurrentError @ react-dom.development.js:25850 performConcurrentWorkOnRoot @ react-dom.development.js:25750 workLoop @ scheduler.development.js:266 flushWork @ scheduler.development.js:239 performWorkUntilDeadline @ scheduler.development.js:533 queue. @ task.js:61 run @ task.js:35 listener @ task.js:46 react-dom.development.js:20013 Uncaught Error: Element type is invalid. Received a promise that resolves to: undefined. Lazy element type must resolve to a class or function. at mountLazyComponent (react-dom.development.js:20013:1) at beginWork (react-dom.development.js:21593:1) at HTMLUnknownElement.callCallback (react-dom.development.js:4164:1) at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:1) at invokeGuardedCallback (react-dom.development.js:4277:1) at beginWork$1 (react-dom.development.js:27451:1) at performUnitOfWork (react-dom.development.js:26557:1) at workLoopSync (react-dom.development.js:26466:1) at renderRootSync (react-dom.development.js:26434:1) at recoverFromConcurrentError (react-dom.development.js:25850:1) mountLazyComponent @ react-dom.development.js:20013 beginWork @ react-dom.development.js:21593 callCallback @ react-dom.development.js:4164 invokeGuardedCallbackDev @ react-dom.development.js:4213 invokeGuardedCallback @ react-dom.development.js:4277 beginWork$1 @ react-dom.development.js:27451 performUnitOfWork @ react-dom.development.js:26557 workLoopSync @ react-dom.development.js:26466 renderRootSync @ react-dom.development.js:26434 recoverFromConcurrentError @ react-dom.development.js:25850 performConcurrentWorkOnRoot @ react-dom.development.js:25750 workLoop @ scheduler.development.js:266 flushWork @ scheduler.development.js:239 performWorkUntilDeadline @ scheduler.development.js:533 queue. @ task.js:61 run @ task.js:35 listener @ task.js:46 react_devtools_backend.js:4012 The above error occurred in the <Route.Provider> component:

at RenderedRoute (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:4840:5)
at Routes (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:5272:5)
at Suspense
at App
at Router (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:5204:15)
at BrowserRouter (http://localhost:4200/vendors-node_modules_react-router-dom_dist_index_js.js:3464:5)

Consider adding an error boundary to your tree to customize error handling behavior. Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries. overrideMethod @ react_devtools_backend.js:4012 logCapturedError @ react-dom.development.js:18687 update.callback @ react-dom.development.js:18720 callCallback @ react-dom.development.js:13923 commitUpdateQueue @ react-dom.development.js:13944 commitLayoutEffectOnFiber @ react-dom.development.js:23391 commitLayoutMountEffects_complete @ react-dom.development.js:24688 commitLayoutEffects_begin @ react-dom.development.js:24674 commitLayoutEffects @ react-dom.development.js:24612 commitRootImpl @ react-dom.development.js:26823 commitRoot @ react-dom.development.js:26682 finishConcurrentRender @ react-dom.development.js:25892 performConcurrentWorkOnRoot @ react-dom.development.js:25809 workLoop @ scheduler.development.js:266 flushWork @ scheduler.development.js:239 performWorkUntilDeadline @ scheduler.development.js:533 queue. @ task.js:61 run @ task.js:35 listener @ task.js:46 react-dom.development.js:26923 Uncaught TypeError: Cannot read properties of undefined (reading 'displayName') at getDisplayName (react_devtools_backend.js:261:19) at getDisplayNameForFiber (react_devtools_backend.js:6381:55) at Object.markComponentErrored (react_devtools_backend.js:5615:29) at markComponentErrored (react-dom.development.js:5053:1) at handleError (react-dom.development.js:26307:1) at renderRootSync (react-dom.development.js:26437:1) at recoverFromConcurrentError (react-dom.development.js:25850:1) at performConcurrentWorkOnRoot (react-dom.development.js:25750:1) at workLoop (scheduler.development.js:266:1) at flushWork (scheduler.development.js:239:1)

我做错了什么或者这里缺少什么?有没有一个例子说明如何使用该文档结合 React 和 Angular?如果您仅使用 React 或 Angular 执行相同的步骤,则可以正常工作。

希望有人可以提供帮助。

Angular 微前端: 在此处输入图像描述

Shell 应用程序: 在此处输入图像描述

React 微前端 在此处输入图片描述

1个回答

我不完全清楚您的配置出了什么问题,但要将不同的技术与模块联合使用,请查看此处:

https://www.angulararchitects.io/aktuelles/multi-framework-and-version-micro-frontends-with-module-federation-your-4-steps-guide/

这里有一个非常好的例子,说明如何将不同的技术结合在一起。 希望对您有所帮助。

heikofritz
2022-12-15