简体   繁体   English

Next.js 的微前端路由问题

[英]Micro frontend routing issue with Next.js

I am building a micro frontend framework with three Next.js projects (app1, app2, base).我正在构建一个包含三个 Next.js 项目(app1、app2、base)的微前端框架。 app1 and app2 are the remote application and base is the host application. app1app2是远程应用程序, base是主机应用程序。

app1 next.config.js : app1 next.config.js

const { withModuleFederation } = require('@module-federation/nextjs-mf');
module.exports = {
  webpack5: true,
  images: {
    domains: ['static.wikia.nocookie.net'],
  },
  webpack: (config, options) => {
    const { isServer } = options;
    const mfConf = {
      mergeRuntime: true,
      name: 'app1',
      library: {
        type: config.output.libraryTarget,
        name: 'app1',
      },
      filename: 'static/runtime/app1RemoteEntry.js',
      remotes: {},
      exposes: {
        './thanatos': './components/thanatos',
      },
    };
    config.cache = false;
    withModuleFederation(config, options, mfConf);
    if (!isServer) {
        config.output.publicPath = 'http://localhost:3001/_next/';
    }

    return config;
  },

  webpackDevMiddleware: (config) => {
    // Perform customizations to webpack dev middleware config
    // Important: return the modified config
    return config;
  },
};

app2 next.config.js app2 next.config.js

const { withModuleFederation } = require('@module-federation/nextjs-mf');
module.exports = {
  webpack5: true,
  images: {
    domains: ['static.wikia.nocookie.net'],
  },
  webpack: (config, options) => {
    const { isServer } = options;
    const mfConf = {
      mergeRuntime: true,
      name: 'app2',
      library: {
        type: config.output.libraryTarget,
        name: 'app2',
      },
      filename: 'static/runtime/app2RemoteEntry.js',
      remotes: {},
      exposes: {
        './zagreus': './components/zagreus',
      },
    };
    config.cache = false;
    withModuleFederation(config, options, mfConf);
    if (!isServer) {
        config.output.publicPath = 'http://localhost:3002/_next/';
    }

    return config;
  },

  webpackDevMiddleware: (config) => {
    // Perform customizations to webpack dev middleware config
    // Important: return the modified config
    return config;
  },
};

base next.config.js基础next.config.js

const { withModuleFederation } = require('@module-federation/nextjs-mf');
const path = require('path');

// For SSR, resolve to disk path (or you can use code streaming if you have access)
// in production use the chunks
const ssrRemoteEntry = (app) =>
  process.env.NODE_ENV === 'production'
    ? path.join(
        `<remotes-path>/${app}/.next/server/chunks/static/runtime/remoteEntry.js`
      )
    : path.resolve(
        __dirname,
        `../${app}/.next/server/static/runtime/remoteEntry.js`
      );

module.exports = {
  webpack5: true,
  images: {
    domains: ['static.wikia.nocookie.net'],
  },
  webpack: (config, options) => {
    const { isServer } = options;
    const mfConf = {
      name: 'base',
      library: {
        type: config.output.libraryTarget,
        name: 'base',
      },
      remotes: {
        app1: isServer ? ssrRemoteEntry('app1') : 'app1',
        app2: isServer ? ssrRemoteEntry('app2') : 'app2',
      },
      exposes: {},
    };
    config.cache = false;
    withModuleFederation(config, options, mfConf);

    return config;
  },

  webpackDevMiddleware: (config) => {
    // Perform customizations to webpack dev middleware config
    // Important: return the modified config
    return config;
  },
};

base _document.js基础_document.js

import Document, { Html, Head, Main, NextScript } from 'next/document';
class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const initialProps = await Document.getInitialProps(ctx);
    return { ...initialProps };
  }

  render() {
    return (
      <Html>
        <script src="http://localhost:3001/_next/static/runtime/app1RemoteEntry.js" />
        <script src="http://localhost:3002/_next/static/runtime/app2RemoteEntry.js" />
        <Head>
          <link rel="icon" href="/favicon.ico" />
          <meta
            name="description"
            content="Demo for Microfrontends using Module Federation"
          />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

export default MyDocument;

When I run the three applications, on base application I can only see the page for base but when I click on the other two buttons want to navigate to app1 and app2 , the browser shows the blank page.当我运行这三个应用程序时,在base应用程序上我只能看到base页面,但是当我单击其他两个按钮想要导航到app1app2时,浏览器会显示空白页面。 在此处输入图像描述

在此处输入图像描述

在此处输入图像描述

I may have the solution for you.我可能有你的解决方案。 What you need to do in the next.config.js is add your URLs to your.env file.在 next.config.js 中您需要做的是将您的 URL 添加到 your.env 文件中。 From there, you tell your base app to navigate to the URL and route.从那里,您告诉您的基础应用程序导航到 URL 并路由。

Here is a sample of what I have done:这是我所做的一个示例:

const { APP1_URL, APP2_URL } = process.env;

module.exports = {
   webpackDevMiddleware: (config) => {
      config.watchOptions = {
         poll: 1000,
         aggregateTimeout: 300,
      };

      return config;
   },
   output: "standalone",
   async rewrites() {
      return [
         {
            source: "/dashboard",
            destination: "/",
         },
         {
            source: "/app1",
            destination: `${APP1_URL}/app1`,
         },
         {
            source: "/app1/:path*",
            destination: `${APP1_URL}/app1/:path*`,
         },
         {
            source: "/app2",
            destination: `${APP2_URL}/app2`,
         },
         {
            source: "/app2/:path*",
            destination: `${APP2_URL}/app2/:path*`,
         },
      ];
   },
};

The tricky part is getting back to your base.棘手的部分是回到你的基地。 Here is a sample for App1's next.config.js:这是 App1 的 next.config.js 的示例:

module.exports = {
   webpackDevMiddleware: (config) => {
      config.watchOptions = {
         poll: 1000,
         aggregateTimeout: 300,
      };

      return config;
   },
   basePath: "/app1",
   output: "standalone",
   async redirects() {
      return [
         {
            source: "/app1/dashboard",
            destination: "/",
            basePath: false,
            permanent: true,
         },
         {
            source: "/app1/app1",
            destination: "/app1",
            basePath: false,
            permanent: true,
         },
      ];
   },
};

Note the webpackDevMiddleware is just for hot refreshing.注意 webpackDevMiddleware 仅用于热刷新。 You don't need it if you don't want.如果你不想要,你就不需要它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM