基于 Next.js 与 Turborepo 的微前端实战:Multi-Zones、共享设计系统与 Monorepo 全解析

📅 发布时间:2026/9/18 13:20:28
基于 Next.js 与 Turborepo 的微前端实战:Multi-Zones、共享设计系统与 Monorepo 全解析
基于 Next.js 与 Turborepo 的微前端实战Multi-Zones、共享设计系统与 Monorepo 全解析【免费下载链接】examplesEnjoy our curated collection of examples and solutions. Use these patterns to build your own robust and scalable applications.项目地址: https://gitcode.com/GitHub_Trending/examples1/examples微前端Microfrontends通过将单体应用拆分为更小、可共享、模块化的组件让多个团队可以彼此独立地开发与交付从而降低单个应用的复杂度并提升开发迭代速度。本仓库的 solutions/microfrontends 示例提供了一个完整可运行的参考实现它用 Turborepo 组织 monorepo用两个独立的 Next.js 应用通过 Multi-Zones 机制共处于同一域名之下并将设计系统、共享组件、工具函数与 ESLint 配置抽离为可独立版本化发布的包。读完本文你将掌握 Multi-Zones 的配置原理、跨 Zone 导航的预取优化手段以及用 Changesets 管理共享包版本与发布的完整流程。概览微前端策略的目标与适用场景微前端的核心目标是把一个体积庞大、难以维护的单体前端拆分为多个可以独立开发、独立部署的模块让不同团队各司其职同时通过共享包保持必要的协作。该 README 明确指出微前端策略的首要目标是缩小单个应用的规模以提升开发速度developer velocity同时允许团队之间继续协作。但微前端并没有唯一的标准答案具体方案取决于你希望如何组织应用与团队。本示例覆盖了以下几种主流策略后续章节将逐一展开Multi-Zones多个独立 Next.js 应用渲染在同一个域名下按路径划分归属共享设计系统用 Tailwind CSS Modules 构建可被多个应用安装使用的组件库Monorepo 与 Polyrepo两种共享代码的组织形态及其对 HMR、版本控制的差异Module Federation面向大型组织、强调交付速度的另一种策略本示例仅作思路介绍不包含实现。仓库结构一览一个由 Turborepo 编排的 monorepo整个示例是一个基于 Turborepo 的 monorepo全部代码使用 TypeScript 编写。核心目录结构如下solutions/microfrontends/ ├── apps/ │ ├── main/ # 主应用负责首页、/about 等页面 │ └── docs/ # 文档应用负责 /docs/** 全部路由 ├── packages/ │ ├── acme-components/ # 跨应用共享的 React 组件如导航栏、跨 Zone 预取 │ ├── acme-design-system/ # 设计系统Button、Quote 等组件CSS Modules Tailwind │ ├── acme-storybook/ # 共享的 Storybook 配置承载设计系统的 stories │ ├── acme-utils/ # 工具函数包 │ └── eslint-config-acme/ # 所有 app 与 package 共用的 ESLint 配置 ├── package.json ├── pnpm-workspace.yaml └── turbo.json从 pnpm-workspace.yaml 可以看到apps/*与packages/*都被注册为 pnpm workspace 成员package.json 中则声明了turbo、changesets/cli、prettier等根级依赖并要求node 22.x与pnpm 9.4.0环境。根目录的 turbo.json 定义了build、dev、lint、test、type-check等任务的依赖关系与输出缓存规则例如build任务通过dependsOn: [^build]保证先构建依赖包再构建应用。快速开始两种使用方式方式一一键部署到 VercelREADME 提供了 Vercel 的一键部署入口其部署参数本身也透露了该仓库的运行约定root-directoryapps/main即以apps/main为部署根目录install-commandpnpm installbuild-commandcd ../.. pnpm build:main即回到 monorepo 根目录执行只构建main应用及其依赖链的脚本ignore-commandnpx turbo-ignore让 Turborepo 判断本次变更是否真正影响该应用从而跳过无效构建。方式二克隆并在本地运行使用create-next-app配合 pnpm 拉取本示例pnpm create next-app --example https://github.com/vercel/examples/tree/main/solutions/microfrontends microfrontends进入目录后在根目录启动开发模式pnpm dev根目录 package.json 中的脚本均为 Turborepo 批量命令pnpm dev等价于turbo run dev会并行启动所有 app 的开发服务器pnpm build等价于turbo run build构建全部应用与包。此外还提供了针对单一应用的定向构建脚本pnpm build:mainturbo run build --filtermain...与pnpm build:docsturbo run build --filterdocs...。关于本地多 Zone 联调apps/main/app/page.tsx 的页面文案还给出了一条实用提示本地开发时可以让部分 Zone 在本地运行、其余 Zone 指向线上环境但指向线上环境的 Zone 不会反映本地代码改动除非你同时把该应用也在本地跑起来。核心机制解析How it WorksMulti-Zones同一域名下的多个独立 Next.js 应用Multi-Zones 是一种将多个彼此独立的 Next.js 应用渲染到同一个域名上的方式适合团队规模大、需要按业务线做关注点分离的场景。它的最佳适用前提是同一域名下存在几个“用户很少跨区频繁跳转”的页面分组。在本示例中apps/main 是主应用apps/docs 是独立应用负责/docs/**的全部路由。关键在于主应用的 next.config.jsconst { DOCS_URL } process.env /** type {import(next).NextConfig} */ const nextConfig { async rewrites() { return [ { source: /docs, destination: ${DOCS_URL}/docs, }, { source: /docs/:path*, destination: ${DOCS_URL}/docs/:path*, }, { source: /docs-static/:path*, destination: ${DOCS_URL}/docs-static/:path*, }, ] }, } module.exports nextConfig这里通过rewrites()把/docs及/docs/**的所有请求代理到由环境变量DOCS_URL指定的文档应用地址。因此用户访问/docs时浏览器地址栏仍停留在主域但实际内容由另一个 Next.js 应用渲染——这就是“多个应用、同一个域名、各自独立构建”的实现方式。第三组 rewrite/docs-static/:path*则用于承接文档应用的静态资源前缀。而 apps/docs/next.config.js 侧需要配合设置assetPrefix: /docs-static把文档应用构建产物的静态资源挂到/docs-static前缀之下并在 Next.js 14 及以下版本通过beforeFiles重写把/docs-static/_next/:path*映射回本应用的/_next/:path*该重写在 Next.js 15 中已不再需要。两个配置文件合在一起才构成完整的 Multi-Zones 资源路由链路。Multi-Zones 的代价与适用边界需要明确的是由于两个 Next.js 应用无法共享 JS bundle、也没有公共 chunk/docs/*与/之间的切换必然是一次整页刷新hard navigationNext.js 自身的客户端预取在这里不生效只能依赖浏览器层面的预取来优化过渡体验。这一点在 apps/main/app/page.tsx 与 apps/docs/app/docs/page.tsx 的页面文案中被反复强调同应用内的页面如 Home 与 About是客户端过渡跨应用如 Home 与 Docs则需要刷新页面。因此官方给出的建议是仅在“页面在逻辑上属于不同应用、但必须服务在同一域名下”时才使用 Multi-Zones。例如一个应用承载落地页、营销页与法律条款页另一个应用承载全部文档页面就是典型的关注点分离用户只会在从主页跳到文档时感受到一次较慢的过渡此时配合target_blank新开标签页是一种不错的体验优化。跨 Zone 导航优化基于 Speculation Rules API 的预取组件为了缓解跨 Zone 整页刷新的性能影响仓库提供了 packages/acme-components/src/prefetch-cross-zone-links.tsx 这个PrefetchCrossZoneLinks组件。它利用浏览器的Speculation Rules API以next/script注入一段speculationrules类型的 JSON 配置const speculationRules { prefetch: [{ source: list, eagerness: moderate, urls: [...hrefs] }], prerender: [ { source: list, eagerness: conservative, urls: [...hrefs] }, ], }prefetch当用户将鼠标悬停在链接上时eagerness: moderate预取目标页面资源prerender在收到pointerdown事件时eagerness: conservative进一步预渲染目标页面从而把跨 Zone 点击后的加载感知降到最低。该组件的实际用法体现在 apps/docs/app/layout.tsx 中文档应用的根布局渲染PrefetchCrossZoneLinks hrefs{[/, /about]} /即提前预取回主应用首页与 About 页的链接。从源码看该组件通过dangerouslySetInnerHTML将序列化后的规则注入Script typespeculationrules是目前在 Next.js 中桥接 Speculation Rules API 的一种轻量实现。设计系统Tailwind 统一 CSS、SWC 负责编译packages/acme-design-system 是一个用CSS Modules Tailwind构建的 React 组件库包含Button、Quote等组件作为依赖被安装进各应用编译步骤由 SWC 处理。以 packages/acme-design-system/src/button/button.tsx 为例组件通过clsx拼接 Tailwind 工具类bg-black text-white、rounded-md、shadow-md等并支持secondary属性切换黑白配色。源码注释特别说明所有这些 Tailwind 类都会被 Next.js 应用中的tailwind.config.js监视——也就是说即便组件源码位于应用之外Tailwind 仍会扫描并生成其样式。这一设计带来两个关键收益CSS 体积不膨胀应用与组件使用的全部 CSS 由 Tailwind 统一处理组件在应用外部并不会额外增加 CSS bundle 大小HMR 与 React Fast Refresh 正常可用尽管组件处于应用之外且走独立的构建流程热更新体验与普通应用内组件一致。每个包的package.json都会声明对设计系统等共享包的依赖构建时由 Turborepo 依据dependsOn: [^build]先编译依赖包再由 SWC 将组件源码编译进应用产物。Monorepo 支持一次改动、自动部署所有受影响应用使用 monorepo 的最大价值在于跨微前端共享代码变得容易当某个被多个应用引用的组件发生改动时开发者只需改一个仓库部署平台会自动为所有受影响的应用触发部署。本示例用Turborepo增强这一体验——通过任务缓存、依赖拓扑排序与--filter定向构建避免重复劳动。此外packages/eslint-config-acme 把统一的代码规范沉淀为共享配置extends: [next, turbo, prettier]并关闭next/next/no-html-link-for-pages规则每个 app 与 package 仅需在自己的.eslintrc中写extends: [acme]即可继承这是 monorepo 中“规范也共享”的典型实践完整配置见 apps/main/app/page.tsx 中的 Snippet 展示。Polyrepos脱离 monorepo 时的版本化协作如果你选择 polyrepo多仓库形态上述工具与思路同样适用但有一个最重要的差异包不在应用仓库内时无法开箱即用地获得包的 HMR。此时的做法是把包作为依赖安装进应用用版本号控制更新若仍想要 HMR则需要借助包管理器链接本地模块例如 pnpm 的pnpm link。一旦公共代码变更就需要提升包版本下游消费者再升级依赖并重新发布应用。Module Federation大型组织的高交付速度选项模块联邦Module Federation是面向大型组织中多个团队、追求交付速度的一种构建策略。README 并未在本示例中给出实现而是建议将其作为团队协作空间有限的大型组织的候选方案自行研究。在评估微前端方案时应结合团队规模、沟通成本与发布节奏综合取舍。包版本管理与发布Changesets 全流程当代码需要在多个仓库之间共享时Changesets 是管理版本、生成 changelog 并发布到 npm 的利器。本示例已预置好相关配置开箱即可开始发布。README 还建议在 GitHub 仓库上安装Changesets bot以更方便地管理贡献者的变更集。生成 Changeset在项目根目录运行pnpm changesetCLI 会依次提出几个问题选择要包含的包展示哪些包有变更、哪些没有默认不包含任何包用space键选中要纳入本次 changeset 的包选择需要 major 版本提升的包同样用space选中如果是首个 major 版本确认是否要发布为变更编写摘要确认 changeset 内容符合预期完成后changeset文件夹中会生成一个 Markdown 文件包含摘要与涉及的包列表。自动发布到 npm示例通过 GitHub Actions 提供名为Release的工作流推送至main分支后自动将 changesets 发布到 npm。启用它需要创建NPM_TOKEN与GITHUB_TOKEN将这两个 Secret 添加到 GitHub 仓库设置中供工作流读取。也可以手动发布pnpm release该命令对应根 package.json 中的脚本turbo run build --filtermain... changeset publish其含义是Turborepo 先为main应用的所有可发布依赖执行build通过--filtermain...排除main应用自身随后changeset publish把新版本发布到 npm。自定义 npm scope示例默认使用acme作为 npm 组织名。要换成你自己的 scope只需三步重命名packages/*下的目录把acme替换为目标 scope全仓库搜索并替换acme为你的 scope重新执行pnpm install。源码导读按图索骥深入实现如果你想进一步研究每个机制的具体实现建议按以下顺序阅读仓库源码Multi-Zones 入口apps/main/next.config.js主域重写与 apps/docs/next.config.jsassetPrefix与资源回写跨 Zone 预取packages/acme-components/src/prefetch-cross-zone-links.tsx以及消费它的 apps/docs/app/layout.tsx设计系统组件packages/acme-design-system/src/button/button.tsx 与同目录下的quote.tsx、.stories.tsxStorybook 故事monorepo 任务编排turbo.json 与根 package.json 的脚本矩阵应用页面演示apps/main/app/page.tsx、apps/main/app/about/page.tsx 与 apps/docs/app/docs/page.tsx它们用实际交互同应用客户端过渡 vs 跨应用整页刷新验证了 Multi-Zones 的边界行为。总而言之这个示例的价值在于它不是纸上谈兵的架构图而是一套可部署、可运行、可发布的最小闭环——从 monorepo 组织、Multi-Zones 路由接入到共享设计系统与跨 Zone 预取优化再到 Changesets 驱动的版本发布流程覆盖了落地微前端所需的大多数关键环节。你可以基于它结合自身团队结构选择其中一种或几种策略组合出适合自己的方案。【免费下载链接】examplesEnjoy our curated collection of examples and solutions. Use these patterns to build your own robust and scalable applications.项目地址: https://gitcode.com/GitHub_Trending/examples1/examples创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考