在 Cloudflare 平台上构建垂直微前端
太平洋时间今天上午 6:55 更新,我们推出了一款新的 垂直微前端(VMFE)Worker 模板。该模板允许你将多个独立的 Cloudflare Workers 映射到单个域名,使团队能够在完全隔离的环境中工作——独立部署营销页面、文档和仪表板——同时向用户呈现一个单一、无缝的应用程序。
大多数微前端架构是“水平”的,意味着单个页面的不同部分从不同服务获取。垂直微前端采用不同的方法,通过 URL 路径来拆分应用程序。在这种模型中,拥有 `/blog` 路径的团队不仅拥有一个组件;他们拥有该路由的整个垂直堆栈——框架、库选择、CI/CD 等。拥有一个路径或一组路径的整个堆栈,使团队能够真正拥有其工作,并自信地部署。
随着团队规模扩大,他们会面临不同框架服务于不同用例的问题。例如,营销网站可能更适合使用 Astro,而仪表板可能更适合 React。或者,假设你有一个单体代码库,多个团队集体部署。来自多个团队添加新功能的更新可能会因为单个团队引入了回归而被令人沮丧地回滚。
我们如何解决向用户隐藏技术实现细节,并让团队在完全自主和控制其领域的情况下提供一致的用户体验的问题?垂直微前端可能是答案。让我们深入探讨它们如何共同解决开发者的痛点。
什么是垂直微前端?
垂直微前端是一种架构模式,其中单个独立团队拥有应用程序功能的整个切片,从用户界面一直到 CI/CD 流水线。这些切片由域名上的路径定义,你可以将单个 Workers 与特定路径关联:
- / = 营销
- /docs = 文档
- /blog = 博客
- /dash = 仪表板
我们可以更进一步,专注于更细粒度的子路径 Worker 关联,例如仪表板。在仪表板中,你可能通过增加 URL 路径深度(例如 `/dash/product-a`)来分割各种功能或产品,在两个产品之间导航可能意味着两个完全不同的代码库。
现在,通过垂直微前端,我们还可以有以下设置:
- /dash/product-a = WorkerA
- /dash/product-b = WorkerB
上述每个路径都是它们自己的前端项目,彼此之间没有共享代码。product-a 和 product-b 路由映射到单独部署的前端应用程序,这些应用程序拥有自己的框架、库、CI/CD 流水线,由各自的团队定义和拥有。
终于,你现在可以端到端地拥有自己的代码了。但现在我们需要找到一种方法来将这些独立项目缝合在一起,更重要的是,让它们感觉像是一个统一的体验。
我们在 Cloudflare 内部也经历了这个痛点,因为仪表板有许多独立团队拥有自己的产品。团队必须面对一个事实:超出他们控制范围的变化会影响用户对其产品的体验。在内部,我们现在为自己的仪表板采用了类似的策略。当用户从核心仪表板导航到我们的 ZeroTrust 产品时,实际上它们是两个完全独立的项目,用户只是通过其路径 `/:accountId/one` 被路由到该项目。
视觉统一体验
将这些独立项目缝合在一起,让它们感觉像是一个统一的体验,并不像你想象的那么困难:只需要几行 CSS 魔法。我们绝对不希望发生的是向用户泄露我们的实现细节和内部决策。如果我们未能让这个用户体验感觉像一个一致的前端,那么我们就对用户造成了严重的不公。
为了实现这种巧妙的手法,让我们稍微了解一下视图过渡和文档预加载是如何发挥作用的。
视图过渡
当我们想要在用户感觉平滑的情况下在两个不同页面之间无缝导航时,视图过渡非常有用。定义页面上的特定 DOM 元素在下一个页面可见之前保持不变,并定义如何处理任何变化,这为多页面应用程序提供了一个非常强大的缝合工具。
然而,在某些情况下,让不同的垂直微前端感觉不同是完全可接受的。例如,我们的营销网站、文档和仪表板可能各自有独特的定义。用户不会期望这三者在导航时感觉一致。但是……如果你决定在单个体验(如仪表板)中引入垂直切片(例如 `/dash/product-a` 和 `/dash/product-b`),那么用户应该永远不知道它们下面是两个不同的仓库/workers/项目。
好了,说得够多了——让我们开始工作。我提到过,让两个独立项目对用户感觉像是一个项目是低成本的,如果你还没听说过 CSS 视图过渡,那么我即将让你大开眼界。如果我告诉你,你可以在不同视图之间(单页面应用 SPA 或多页面应用 MPA)制作动画过渡,让它们感觉像是一个,你会怎么想?
在添加任何视图过渡之前,如果我们在两个不同 Workers 拥有的页面之间导航,中间加载状态将是浏览器中的白色空白屏幕,持续几百毫秒,直到下一个页面开始完全渲染。页面不会感觉一致,当然也不会感觉像单页面应用程序。
显示为每个站点之间的多个导航元素。
如果我们希望元素保持不变,而不是看到白色空白页面,我们可以通过定义 CSS 视图过渡来实现。使用下面的代码,我们告诉当前文档页面,当视图过渡事件即将发生时,保持 nav DOM 元素在屏幕上,如果现有页面和目标页面之间存在外观差异,那么我们将使用 ease-in-out 过渡来动画化它。突然之间,两个不同的 Workers 感觉像是一个。
- @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 不支持推测规则,但 Chrome/Edge/Opera 支持这个较新的功能。推测规则 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 B。
进一步分解,路由器 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 中定义,然后告诉我们允许向三个独立的额外 Workers(营销、文档和仪表板)发出请求。授予权限就这么简单,但让我们深入一些更复杂的逻辑,包括请求路由和 HTML 重写网络响应。
请求路由
了解了我们可以在需要时调用的各种其他 Workers 后,现在我们需要一些逻辑来知道何时将网络请求定向到哪里。由于路由器 Worker 被分配到我们的自定义域名,所有传入请求首先在网络边缘命中它。然后它确定哪个 Worker 应该处理请求并管理响应。
第一步是将 URL 路径映射到关联的 Workers。当收到某个请求 URL 时,我们需要知道它需要转发到哪里。我们通过定义规则来实现这一点。虽然我们支持通配符路由、动态路径和参数约束,但我们将专注于基础——字面路径前缀——因为它更清楚地说明了这一点。
在这个例子中,我们有三个微前端:
- / = 营销
- /docs = 文档
- /dash = 仪表板
上述每个路径都需要映射到一个实际的 Worker(参见上面部分中我们 wrangler 定义的服务)。对于我们的路由器 Worker,我们定义一个额外的变量,包含以下数据,以便我们知道哪些路径应该映射到哪些服务绑定。
我们现在知道在请求到来时将用户路由到哪里!定义一个名为 ROUTES 的 wrangler 变量,内容如下:
- {
- "routes":[
- {"binding": "HOME", "path": "/"},
- {"binding": "DOCS", "path": "/docs"},
- {"binding": "DASH", "path": "/dash"}
- ]
- }
复制代码
让我们设想一个用户访问我们网站的路径 `/docs/installation`。在底层,发生的是请求首先到达我们的路由器 Worker,它负责理解哪些 URL 路径映射到哪些单个 Workers。它理解 `/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 变量中,如果你在根级别将 smoothTransitions 设置为 true,那么 CSS 过渡视图代码将自动添加。此外,如果你将路由内的 preload 键设置为 true,那么该路由的脚本代码推测规则也将自动添加。
下面是一个两者都在作用的例子:
- {
- "smoothTransitions":true,
- "routes":[
- {"binding": "APP1", "path": "/app1", "preload": true},
- {"binding": "APP2", "path": "/app2", "preload": true}
- ]
- }
复制代码
开始使用
你今天就可以开始使用垂直微前端模板进行构建。访问 Cloudflare 仪表板深层链接或转到“Workers & Pages”并点击“Create application”按钮开始。从那里,点击“Select a template”,然后“Create microfrontend”,你就可以开始配置你的设置了。
查看文档以了解如何映射你现有的 Workers 并启用视图过渡。我们迫不及待想看到你在边缘构建的复杂、多团队应用程序!
原文链接:Building vertical microfrontends on Cloudflare’s platform |