Module Federation或Micro Frontend在Next js 12.2.0版本中不起作用



我遵循以下步骤在下一个js中实现模块联合。

  1. 在src/node/components文件夹内的存储库cm保险网中创建一个组件Insurance_Detail.tsx,该组件将被公开。下面是next.config.js文件
const assetPrefix = '/jobs-assets';
const nextConfig = {
assetPrefix,
env: {
assetPrefix
},
experimental: {
images: {
unoptimized: true
}
},
reactStrictMode: true,
webpack5: true,
srcDir: 'src/node/',
//distDir: 'build',
webpack: (config, options) => { // webpack configurations
config.plugins.push(
new options.webpack.container.ModuleFederationPlugin({
name:"InsuranceA",
filename: "static/chunks/pages/cm_insurance_web.js", // remote file name which will used later
remoteType: "var",
exposes: { // expose all component here.
**"./InsuranceDetail": "./components/Insurance_Details.tsx"**
},
shared: [
{
react: {
eager: true,
singleton: true,
requiredVersion: false,
}
},
{
"react-dom": {
eager: true,
singleton: true,
requiredVersion: false,
}
},
]
})
)
config.cache = false;
config.output.publicPath = 'http://localhost:3000/_next/';
return config
}
}
module.exports = nextConfig
  1. 当我们使用命令npm run build构建repo-cm保险web时,我们可以看到javascript文件正在src/node/.next/static/cchunks/pages/cm_insurance_web.js中创建。此项目repo在localhost上的3000端口上运行。

  2. 现在需要在其他存储库中使用这个javascript,比如cm job board web。让我们创建消费者应用程序。下面是它的next.config.js文件

/** @type {import('next').NextConfig} */
const assetPrefix = '/jobs-assets';
const path = require('path');
const nextConfig = {
assetPrefix,
env: {
assetPrefix
},
basePath: '/search-jobs',
experimental: {
images: {
unoptimized: true
}
},
reactStrictMode: true,
srcDir: 'src/node/',
webpack: (config, options) => {
config.plugins.push(
new options.webpack.container.ModuleFederationPlugin({
name:"jobboardWeb",
filename: "static/chunks/cm_job_board_web.js",
remoteType: "var",
remotes: {
InsuranceA: JSON.stringify('InsuranceA@http://localhost:3000/jobs-assets/_next/static/chunks/pages/cm_insurance_web.js')
},exposes: {},
shared: [
{
react: {
eager: true,
singleton: true,
requiredVersion: false,
}
},
{
"react-dom": {
eager: true,
singleton: true,
requiredVersion: false,
}
},
]
})
)
config.cache = false;
return config
},
webpack5: true
}
module.exports = nextConfig
  1. 在消费者应用程序的_app.tsx文件中添加脚本标记,如下所示:
import { AppProps } from "next/app";
import "bootstrap/dist/css/bootstrap.css";
import "../styles/globals.scss";
import Layout from "../components/layout";
import { persistor, store } from "../store/store";
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react";
import Authentication from "../config/auth.gaurd";
import Head from "next/head";
import React from "react";
import Script from "next/script";
function MyApp({ Component, pageProps }: AppProps) {
return (
<Layout>
<>
<Script src="http://localhost:3000/jobs-assets/_next/static/chunks/pages/cm_insurance_web.js" />
<Head>
<link rel="preconnect" href="https://fonts.googleapis.com"/>
<link rel="preconnect" href="https://fonts.gstatic.com"/>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@100;200;300;400;500;600;700&display=swap"/>
<link rel="shortcut icon" href="/favicon2.ico"/>
<title>Jobboard Search</title>
</Head>
<Script id="gtm-script" strategy="afterInteractive">
{`(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-TKJH8RR');`
}
</Script>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<Authentication>
<noscript dangerouslySetInnerHTML={{ __html:
`<iframe src="https://www.googletagmanager.com/ns.html?id=GTM-TKJH8RR"
height="0" width="0" style="display:none;visibility:hidden"></iframe>
`}}>
</noscript>
<Component {...pageProps} />
</Authentication>
</PersistGate>
</Provider>
</>
</Layout>
);
}
export default MyApp;
  1. 让我们将该模块导入index.tsx文件并使用它
import {NextPage} from "next";
import React, {lazy, Suspense, useState} from "react";
import dynamic from 'next/dynamic'

const InsuranceDetail2 = dynamic(() => import(('InsuranceA/InsuranceDetail')), {
ssr: false
}) as NextPage;
const Insurance: NextPage = ({}: any) => {
return (
<InsuranceDetail2 />
)
}

export default Insurance

在完成上述步骤后,我可以看到远程js文件正在浏览器的网络选项卡中加载,但远程组件并没有被渲染,而是变成了空白页面。

请查看随附的截图

如果我遗漏了什么,请告诉我。我从下面的链接中获得了参考资料。

  1. https://blog.logrocket.com/micro-frontend-react-next-js/
  2. https://blog.logrocket.com/building-micro-frontends-webpacks-module-federation/
  3. https://dev.to/omher/building-react-app-with-module-federation-and-nextjsreact-1pkh

这是一个快速变化的包,因此信息很快就会过时,并且非常特定于您使用的版本。但在快速浏览了一下您的配置后,以下是我的一些建议我假设您使用的是nextjs mf包的第6版

  1. 我认为您不需要在共享配置中做出反应
  2. remoteType: "var"不必要
  3. 将文件名设置为"static/chunks/remoteEntry.js"
  4. 您可能想要在额外选项中设置automaticAsyncBoundary: true

如果您在主机中获取远程入口文件,则意味着您的网络配置或多或少配置正常。如果组件未呈现,则可以尝试检查远程入口文件中要公开的组件或模块。看看里面有没有。然后,如果它在那里,你应该确保格式是正确的(esm或commonjs(。如果您正在连接两个nextjs应用程序,那么它应该只需最少的干预即可工作,因此请删除远程类型设置。就让插件来做吧。

这样配置遥控器:

remotes: {
remote: `remote@http://localhost:8080/_next/static/${
isServer ? 'ssr' : 'chunks'
}/remoteEntry.js`,
},

安装nextjs mf⚠️注意:要使应用程序使用模块联合功能,您需要访问https://app.privjs.com/package?pkg=@模块联盟/nextjs mf[[nextjs ssr^]插件,目前需要付费许可证!

要安装该工具,我们需要登录〔PrivJs〕(https://privjs.com/^)要使用npm,请运行以下命令:

npm登录--注册表https://r.privjs.com

完成后,包含凭据的文件将保存在~/.npmrc中。现在,您可以使用以下命令安装nextjs-mf:

npm install@module federation/nextjs mf—注册表https://r.privjs.com

所以模块联盟是nextjs中的付费模块,在付费模块的帮助下,我能够实现它

  1. 保险模块的next.config.js
/** @type {import('next').NextConfig} */
const NextFederationPlugin = require('@module-federation/nextjs-mf');
const assetPrefix = '/jobs-assets';
const nextConfig = {
assetPrefix,
env: {
assetPrefix
},
reactStrictMode: true,
webpack5: true,
srcDir: 'src/node/',
//distDir: 'build',
webpack: (config, options) => { // webpack configurations
if (!options.isServer) {
config.plugins.push(
new NextFederationPlugin({
name: "insurancea",
filename: "static/chunks/pages/cm_insurance_web.js", // remote file name which will used later
exposes: { // expose all component here.
"./insurancedetail": "./components/Insurance_Details.tsx"
},
shared:
{
react: {
singleton: true,
requiredVersion: false,
}
}
}),
);
}
return config
}
};
module.exports = nextConfig
  1. 在package.json中添加模块联合的依赖项
"dependencies": {
"@module-federation/nextjs-mf": "^5.9.2",
}
  1. 在同一保险模块的_app.tsx文件中添加导入
import '@module-federation/nextjs-mf/src/include-defaults';

这就是曝光组件

  1. 现在在next.config.js中为远程组件(消费者应用程序-cm工作板web(更新它
/** @type {import('next').NextConfig} */
const NextFederationPlugin = require('@module-federation/nextjs-mf');
const assetPrefix = '/jobs-assets';
const path = require('path');
const nextConfig = {
assetPrefix,
env: {
assetPrefix
},
basePath: '/search-jobs',
reactStrictMode: true,
srcDir: 'src/node/',
webpack: (config, options) => {
if (!options.isServer) {
config.plugins.push(
new NextFederationPlugin({
name: "jobboardWeb",
filename: "static/chunks/cm_job_board_web.js",
remotes: {
//  cm_insurance_web: options.isServer ? 'http://localhost:3000/jobs-assets/_next/static/chunks/cm_insurance_web.js' : 'fe1'
insurancea: 'insurancea@http://localhost:3000/jobs-assets/_next/static/chunks/pages/cm_insurance_web.js'
}, exposes: {},
shared: {}
}),
);
}
return config
},
webpack5: true
};
module.exports = nextConfig
  1. 在消费者应用程序的package.json中添加模块联盟的依赖项
"dependencies": {
"@module-federation/nextjs-mf": "^5.9.2",
}
  1. 在消费者应用程序的_app.tsx文件中添加导入
import '@module-federation/nextjs-mf/src/include-defaults';
  1. 最终将该模块导入index.tsx文件,并在消费者应用程序中使用
import { Suspense } from 'react'
import React from 'react'
import dynamic from 'next/dynamic'
const DynamicComponent4 = dynamic(
() => import('insurancea/insurancedetail'),
{ loading: () => <p>Loading caused by client page transition ...</p>, ssr: false }
)

export default function Insurance() {
return (
<div>
<DynamicComponent4 />
</div>
)
}

就是这样。

最新更新