Я выполнил следующие шаги, чтобы добиться объединения модулей в следующем 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
Когда мы собираем репо cm-insurance-web с помощью команды npm run build, мы видим, что файл javascript создается внутри src/node/.next/static/chunks/pages/cm_insurance_web.js. Этот репозиторий проекта работает на порту 3000 на локальном хосте.
Теперь нужно использовать этот 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
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;
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 загружается на вкладке сети браузера, но удаленный компонент не отображается и получает пустую страницу.
Пожалуйста, найдите прикрепленный скриншот
Пожалуйста, дайте мне знать, если я что-то пропустил здесь. Я взял ссылку из ссылок ниже.
Установка nextjs-mf ⚠️ Внимание: для работы приложения с функциями Module Federation вам необходимо иметь доступ к https://app.privjs.com/package?pkg=@module-federation/nextjs-mf[[nextjs-ssr^] плагин, который в настоящее время требует платной лицензии!
Чтобы установить инструмент, нам нужно войти в [PrivJs}(https://privjs.com/^) с помощью npm, для этого выполните следующую команду:
npm логин --registry https://r.privjs.com
Как только это будет сделано, файл, содержащий ваши учетные данные, будет сохранен в ~/.npmrc. Теперь вы можете установить nextjs-mf с помощью следующей команды:
npm install @module-federation/nextjs-mf --registry https://r.privjs.com
поэтому федерация модулей является платным модулем в следующем 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
"dependencies": {
"@module-federation/nextjs-mf": "^5.9.2",
}
import '@module-federation/nextjs-mf/src/include-defaults';
Вот и все, что касается компонента expose
/** @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
"dependencies": {
"@module-federation/nextjs-mf": "^5.9.2",
}
import '@module-federation/nextjs-mf/src/include-defaults';
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>
)
}
Вот и все.
Это устарело, исходный код проекта был полностью открыт в июле 2021 года, и платного уровня больше нет github.com/module-federation/module-federation-examples/…