Menu

How to build micro-frontends using multi-zones and Next.js

示例

Multi-Zones 是一种微前端的实现方式,它将一个域名上的大型应用拆分为多个小型 Next.js 应用,每个应用负责处理一组特定路径。当应用中存在与其他页面无关的页面集合时,这种方法非常有用。通过将这些页面移至单独的区域(即独立的应用),你可以减小每个应用的体积,从而改善构建时间,并移除只在某个区域需要的代码。由于应用之间是解耦的,Multi-Zones 还允许域名上的其他应用使用自己选择的框架。

例如,假设你有以下一组需要拆分的页面:

  • /blog/* 用于所有博客文章
  • /dashboard/* 用于用户登录到仪表盘后的所有页面
  • /* 用于网站上其他未被其他区域覆盖的部分

借助 Multi-Zones 支持,你可以创建三个应用,它们都在同一域名下提供服务,对用户来说看起来是一样的,但你可以独立开发和部署每个应用。

三个区域:A、B、C。展示了不同区域路由之间的硬导航,以及同一区域内路由之间的软导航。

在同一区域内的页面之间导航将执行软导航,即不需要重新加载页面的导航。例如,在这个图表中,从 / 导航到 /products 将是一个软导航。

从一个区域的页面导航到另一个区域的页面,比如从 //dashboard,将执行硬导航,卸载当前页面的资源并加载新页面的资源。经常一起访问的页面应该位于同一区域内,以避免硬导航。

如何定义区域

区域是一个普通的 Next.js 应用,你还需要配置 assetPrefix 以避免与其他区域的页面和静态文件冲突。

next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  assetPrefix: '/blog-static',
}

Next.js 资源,如 JavaScript 和 CSS,将以 assetPrefix 为前缀,以确保它们不会与其他区域的资源冲突。这些资源将在每个区域的 /assetPrefix/_next/... 下提供。

处理所有未路由到其他更具体区域的路径的默认应用不需要 assetPrefix

在早于 Next.js 15 的版本中,你可能还需要额外的重写来处理静态资源。在 Next.js 15 中,这不再是必需的。

next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  assetPrefix: '/blog-static',
  async rewrites() {
    return {
      beforeFiles: [
        {
          source: '/blog-static/_next/:path+',
          destination: '/_next/:path+',
        },
      ],
    }
  },
}

如何将请求路由到正确的区域

使用 Multi Zones 设置后,你需要将路径路由到正确的区域,因为它们由不同的应用提供服务。你可以使用任何 HTTP 代理来完成这项工作,但其中一个 Next.js 应用也可以用来路由整个域名的请求。

要使用 Next.js 应用路由到正确的区域,你可以使用 rewrites。对于每个由不同区域提供服务的路径,你需要添加一个重写规则,将该路径发送到其他区域的域名。例如:

next.config.js
async rewrites() {
    return [
        {
            source: '/blog',
            destination: `${process.env.BLOG_DOMAIN}/blog`,
        },
        {
            source: '/blog/:path+',
            destination: `${process.env.BLOG_DOMAIN}/blog/:path+`,
        }
    ];
}

destination 应该是由该区域提供服务的 URL,包括协议和域名。这应该指向该区域的生产域名,但也可以用于在本地开发中将请求路由到 localhost

值得注意的是:URL 路径应该对一个区域是唯一的。例如,两个区域尝试提供 /blog 会创建路由冲突。

使用中间件路由请求

通过 rewrites 路由请求是推荐的方式,可以最小化请求的延迟开销,但当路由需要动态决策时,也可以使用中间件。例如,如果你正在使用功能标志来决定路径应该路由到哪里,比如在迁移期间,你可以使用中间件。

middleware.js
export async function middleware(request) {
  const { pathname, search } = req.nextUrl;
  if (pathname === '/your-path' && myFeatureFlag.isEnabled()) {
    return NextResponse.rewrite(`${rewriteDomain}${pathname}${search});
  }
}

区域之间的链接

链接到不同区域的路径应该使用 a 标签,而不是 Next.js 的 <Link> 组件。这是因为 Next.js 会尝试预取并软导航到 <Link> 组件中的任何相对路径,这在跨区域时无法工作。

共享代码

构成不同区域的 Next.js 应用可以存在于任何仓库中。然而,通常将这些区域放在 monorepo 中更方便共享代码。对于存在于不同仓库中的区域,代码也可以通过公共或私有的 NPM 包共享。

由于不同区域的页面可能会在不同时间发布,功能标志对于在不同区域之间同时启用或禁用功能非常有用。

Server Actions

当在 Multi-Zones 中使用 Server Actions 时,你必须明确允许面向用户的源,因为你面向用户的域名可能提供多个应用。在你的 next.config.js 文件中,添加以下行:

next.config.js
const nextConfig = {
  experimental: {
    serverActions: {
      allowedOrigins: ['your-production-domain.com'],
    },
  },
}

查看 serverActions.allowedOrigins 以获取更多信息。