普通视图

发现新文章,点击刷新页面。
今天 — 2026年2月2日首页

在 Cloudflare 平台上构建垂直微前端

作者 程序猿DD
2026年2月2日 11:23

想象一下,你正在开发一个大型Web应用。营销团队想要用Astro构建他们的页面以获得最佳的SEO效果,而产品团队却坚持要用React来构建功能丰富的后台管理系统。更糟糕的是,每次发布新版本时,十几个团队的代码都需要一起打包、一起测试、一起上线——只要其中一个团队引入了一个bug,整个发布就要回滚。这种"一荣俱荣、一损俱损"的耦合方式,是不是让你感到无比头疼?

或者,你的公司刚刚收购了一个创业公司,他们的产品是用Vue写的,而你们的主站是用React写的。你想把他们的功能整合进来,但又不希望把两个完全不同的代码库强行混在一起。

这些都是现代Web开发中真实存在的难题。传统的微前端架构通常是"水平"的——同一个页面上的不同组件来自不同的服务。但如果有一种方式,能让每个团队完全独立地开发、部署和维护自己的功能模块,而用户却感觉在使用一个无缝的、统一的应用呢?

这就是垂直微前端(Vertical Microfrontends)要解决的问题。现在,Cloudflare推出了一款全新的Worker模板,让这种架构变得前所未有的简单。

什么是垂直微前端?

垂直微前端是一种架构模式,单个独立团队拥有应用程序功能的完整切片,从用户界面一直到底层的CI/CD流水线。这些切片通过域名上的路径来定义,你可以将各个独立的Worker与特定路径关联起来:

/      = 营销网站
/docs  = 文档
/blog  = 博客
/dash  = 仪表盘

我们还可以进一步细化,在更细粒度的子路径上关联不同的Worker。比如在仪表盘中,你可能通过各种功能或产品来划分URL路径的深度(例如 /dash/product-a),在两个产品之间导航可能意味着两个完全不同的代码库。

现在有了垂直微前端,我们还可以这样设计:

/dash/product-a  = WorkerA
/dash/product-b  = WorkerB

上面的每个路径都是独立的前端项目,它们之间没有任何共享代码。product-aproduct-b 路由映射到分别部署的前端应用,它们有自己的框架、库、CI/CD流水线,由各自的团队定义和拥有。

你可以端到端地拥有自己的代码。但现在我们需要找到一种方法将这些独立的项目缝合在一起,更重要的是,让它们感觉像是一个统一的体验。

Cloudflare自己也在经历这个痛点,因为仪表盘有许多独立的团队负责各自的产品。团队必须面对一个事实:在他们控制范围之外所做的更改会影响用户对其产品的体验。

在内部,我们现在对自己的仪表盘也采用了类似的策略。当用户从核心仪表盘导航到我们的ZeroTrust产品时,实际上它们是两个完全独立的项目,用户只是通过路径 /:accountId/one 被路由到那个项目。

视觉上的统一体验

将这些独立项目缝合在一起,让它们感觉像一个统一的体验,并没有你想象的那么困难:只需要几行CSS魔法。我们绝对不希望发生的事情是将我们的实现细节和内部决策泄露给用户。如果我们无法让这个用户体验感觉像一个统一的前端,那我们就对用户犯下了严重的错误。

要实现这种巧妙的手法,让我们先了解一下视图过渡和文档预加载是如何发挥作用的。

视图过渡

当我们想要在两个不同页面之间无缝导航,同时让最终用户感觉流畅时,视图过渡非常有用。在页面上定义特定的DOM元素,让它们一直保留到下一页可见,并定义任何变化的处理方式,这成为了多页应用的强大缝合工具。

然而,在某些情况下,让各个垂直微前端感觉不同也是完全可以接受的。比如我们的营销网站、文档和仪表盘,它们各自都有独特的定义。用户不会期望这三者在导航时都感觉统一。但是……如果你决定在单个体验中引入垂直切片(例如 /dash/product-a/dash/product-b),那么用户绝对不应该知道它们底层是两个不同的仓库/Worker/项目。

好了,说得够多了——让我们开始动手吧。我说过让两个独立的项目对用户来说感觉像是一个是低成本的,如果你还没有听说过CSS视图过渡,那么接下来我要让你大开眼界了。

如果我告诉你,你可以在单页应用(SPA)或多页应用(MPA)的不同视图之间创建动画过渡,让它们感觉像是一个整体?在添加任何视图过渡之前,如果我们导航属于两个不同Worker的页面,中间加载状态会是浏览器中的白色空白屏幕,持续几百毫秒,直到下一页开始渲染。页面不会感觉统一,当然也不会像单页应用。

如果希望元素保留,而不是看到白色空白页,我们可以通过定义CSS视图过渡来实现。通过下面的代码,我们告诉当前文档页面,当视图过渡事件即将发生时,将nav DOM元素保留在屏幕上,如果现有页面和目标页面之间存在任何外观差异,我们将使用ease-in-out过渡来动画展示。

突然之间,两个不同的Worker感觉就像一个了。

@supports (view-transition-name: none) {
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation-duration: 0.3s;
    animation-timing-function: ease-in-out;
  }
  nav { view-transition-name: navigation; }
}

预加载

在两个页面之间过渡让它"看起来"无缝——我们还希望它"感觉"像客户端SPA一样即时。虽然目前Firefox和Safari不支持Speculation Rules,但Chrome/Edge/Opera确实支持这个较新的API。Speculation Rules API旨在提高未来导航的性能,特别是对于文档URL,让多页应用感觉更像单页应用。

分解成代码,我们需要定义一个特定格式的脚本规则,告诉支持的浏览器如何预取与我们Web应用程序连接的其他垂直切片——可能通过某些共享导航链接。

<script type="speculationrules">
  {
    "prefetch": [
      {
        "urls": ["https://product-a.com", "https://product-b.com"],
        "requires": ["anonymous-client-ip-when-cross-origin"],
        "referrer_policy": "no-referrer"
      }
    ]
  }
</script>

有了这些,我们的应用程序会预取其他微前端并将它们保留在内存缓存中,所以如果我们导航到那些页面,会感觉几乎是即时的。

对于明显可区分的垂直切片(营销、文档、仪表盘),你可能不需要这样做,因为用户在它们之间导航时会预期有轻微的加载。然而,当垂直切片定义在特定可见体验内时(例如在仪表盘页面中),强烈建议使用。

通过视图过渡和推测规则,我们能够将完全不同的代码仓库联系在一起,感觉就像它们来自单页应用一样。如果你问我,这太神奇了。

零配置请求路由

现在我们需要一种机制来托管多个应用程序,以及一种在请求流入时将它们缝合在一起的方法。定义一个Cloudflare Worker作为"路由器",允许在边缘的单个逻辑点处理网络请求,然后将它们转发给负责该URL路径的垂直微前端。而且我们可以将单个域名映射到该路由器Worker,其余的就"正常工作"了。

服务绑定

如果你还没有探索过Cloudflare Worker服务绑定,那么值得花点时间了解一下。

服务绑定允许一个Worker调用另一个Worker,而无需经过公开可访问的URL。服务绑定允许Worker A调用Worker B上的方法,或将请求从Worker A转发到Worker。进一步分解,路由器Worker可以调用已定义的每个垂直微前端Worker(例如营销、文档、仪表盘),假设它们都是Cloudflare Workers。

这为什么重要?这正是将这些垂直切片"缝合"在一起的机制。我们将在下一节深入探讨请求路由如何处理流量分割。但要定义这些微前端中的每一个,我们需要更新路由器Worker的wrangler定义,这样它就知道允许调用哪些前端。

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "router",
  "main": "./src/router.js",
  "services": [
    {
      "binding": "HOME",
      "service": "worker_marketing"
    },
    {
      "binding": "DOCS",
      "service": "worker_docs"
    },
    {
      "binding": "DASH",
      "service": "worker_dash"
    }
  ]
}

上面的示例定义在我们的路由器Worker中,然后告诉我们被允许向三个独立的额外Worker(营销、文档和仪表盘)发出请求。授予权限就这么简单,但让我们深入研究一些更复杂的逻辑,包括请求路由和HTML重写网络响应。

请求路由

了解了在需要时可以调用的各种其他Worker之后,现在我们需要一些逻辑来确定何时将网络请求定向到哪里。由于路由器Worker被分配到我们的自定义域名,所有传入的请求首先在网络边缘到达它。然后它确定哪个Worker应该处理请求,并管理结果响应。

第一步是将URL路径映射到关联的Worker。当收到某个请求URL时,我们需要知道它需要被转发到哪里。我们通过定义规则来实现这一点。虽然我们支持通配符路由、动态路径和参数约束,但我们将专注于基础——字面路径前缀——因为它更清楚地说明了要点。

在这个例子中,我们有三个微前端:

/      = 营销
/docs  = 文档
/dash  = 仪表盘

上面的每个路径都需要映射到一个实际的Worker(参见上面章节中的wrangler服务定义)。对于我们的路由器Worker,我们定义一个额外的变量,包含以下数据,这样我们就知道哪些路径应该映射到哪些服务绑定。现在我们知道当请求进来时应该将用户路由到哪里!定义一个名为ROUTES的wrangler变量,内容如下:

{
  "routes": [
    {"binding": "HOME", "path": "/"},
    {"binding": "DOCS", "path": "/docs"},
    {"binding": "DASH", "path": "/dash"}
  ]
}

让我们设想一个用户访问我们网站的路径 /docs/installation。在底层,发生的情况是请求首先到达我们的路由器Worker,它负责了解什么URL路径映射到哪个独立的Worker。它理解 /docs 路径前缀映射到我们的 DOCS 服务绑定,参照我们的wrangler文件指向我们的 worker_docs 项目。我们的路由器Worker知道 /docs 被定义为垂直微前端路由,从路径中移除 /docs 前缀,将请求转发给我们的 worker_docs Worker来处理请求,然后最终返回我们得到的任何响应。

为什么要删除 /docs 路径呢?这是一个实现细节的选择,目的是当Worker通过路由器Worker访问时,它可以清理URL来处理请求,就像它是从路由器Worker外部调用的一样。像任何Cloudflare Worker一样,我们的 worker_docs 服务可能有自己的独立URL可以访问。我们决定希望该服务URL继续独立工作。当它附加到我们的新路由器Worker时,它会自动处理移除前缀,这样服务就可以从自己定义的URL或通过我们的路由器Worker访问……任何地方都可以,无所谓。

HTMLRewriter

用URL路径分割我们的各种前端服务(例如 /docs/dash)让我们很容易转发请求,但当我们的响应包含不知道它被通过路径组件反向代理的HTML时……嗯,这就会出问题。

假设我们的文档网站在响应中有一个图片标签 <img src="./logo.png" />。如果我们的用户正在访问页面 https://website.com/docs/,那么加载 logo.png 文件可能会失败,因为我们的 /docs 路径只是由我们的路由器Worker人为定义的。

只有当我们的服务通过路由器Worker访问时,我们才需要对一些绝对路径进行HTML重写,这样我们返回的浏览器响应才能引用有效的资源。实际上发生的是,当请求通过我们的路由器Worker时,我们将请求传递给正确的服务绑定,并从中接收响应。在将其传回客户端之前,我们有机会重写DOM——所以在看到绝对路径的地方,我们继续用代理路径预先填充它。以前我们的HTML返回的图片标签是 <img src="./logo.png" />,现在我们修改为在返回客户端浏览器之前 <img src="./docs/logo.png" />

让我们回到CSS视图过渡和文档预加载的魔法。我们当然可以把那段代码手动放到我们的项目中并让它工作,但这个路由器Worker也会使用HTMLRewriter自动为我们处理这些逻辑。

在你的路由器Worker ROUTES 变量中,如果你在根级别设置 smoothTransitionstrue,那么CSS过渡视图代码会自动添加。此外,如果你在路由中设置 preload 键为 true,那么该路由的推测规则脚本代码也会自动添加。

下面是两者结合使用的示例:

{
  "smoothTransitions": true,
  "routes": [
    {"binding": "APP1", "path": "/app1", "preload": true},
    {"binding": "APP2", "path": "/app2", "preload": true}
  ]
}

开始使用

你今天就可以开始使用垂直微前端模板构建了。

访问Cloudflare仪表盘的链接,或者进入"Workers & Pages"并点击"创建应用程序"按钮开始。从那里,点击"选择模板"然后"创建微前端",你就可以开始配置你的设置了。

更多使用指南,可以点击查看文档 ,如果您对各种云原生架构的内容感兴趣,也可以关注我的博客:程序猿DD,第一时间获得干货更新。

❌
❌