基于 React 的微前端

2025-05-24

基于 React 的微前端

近年来,“微前端”一词逐渐成为科技主流。虽然实际实现微前端的模式有很多,但我们认为可能存在一种“理想”的解决方案——一种能够兼具单体架构优势和使用独立模块优势的解决方案。

在本文中,我们将探讨一个基于 React 构建的微前端解决方案,该方案支持无限扩展开发、渐进式部署以及遵循无服务器基础设施。我们的解决方案由一个应用外壳和独立开发的模块组成,这些模块动态集成到应用外壳中。

我们将使用的解决方案名为Piral,它是我们前端模块化架构的参考实现。该前端架构的定义基于我们过去三年在多个客户项目中积累的实际经验。

模态

考虑单体应用和微应用(称为 Modulith)之间的交集的方法的优点在于,我们可以实现诸如

  • 逐步采用(为了方便迁移)
  • 共享库(例如模式库),或
  • 现有的布局/应用程序框架。

以上只是可能性。缺点在于采用这些选项时需要承担一些责任,例如,在应用外壳中包含共享库将导致经典的依赖管理问题。

模块化与微前端有何关系?下面我们展示了一种可能的微前端设计——每个服务都有一个关联的微前端。每个微前端都代表一个独立的单元,可能拥有自己的模式库和技术。

微前端

相比之下,Modulith 尝试复用负责用户体验的重要部分。因此,一致性至关重要。当然,这种方法也会带来一些挑战,但一致性和冗余之间的权衡正是创建前端 UI 与创建后端服务的不同之处。

模量

上图展示了模块的新增功能,它提供了一个与总体职责相关的边界框。入口点是应用程序外壳。

应用程序外壳

通常,创建一个利用微前端的新应用,首先要搭建一个应用外壳 (App Shell)。应用外壳包含共享布局、一些核心业务功能(如果有)以及共享依赖项。应用外壳还负责设置所有模块(在Piral中称为 Pilet)需要遵循的基本规则。

在最简单的示例中,应用程序外壳可能如下所示:

import * as React from "react";
import { render } from "react-dom";
import { Redirect } from "react-router-dom";
import { createPiral, Piral, SetRoute } from "piral";

const piral = createPiral({
  requestPilets() {
    return fetch("https://feed.piral.io/api/v1/pilet/mife-demo")
      .then(res => res.json())
      .then(res => res.items);
  }
});

const app = <Piral instance={piral} />;

render(app, document.querySelector("#app"));

这将创建一个空白的应用程序外壳,它已经允许将不同的页面和片段拼接在一起。

太好了,那么我们应该如何部署这个应用程序呢?这里有两件事要做:

  1. 构建(即捆绑)应用程序并将其推送到某些存储。
  2. 打包源代码并将其推送到(私有)仓库。或者:共享 tarball。

第一步确保我们的应用程序可以通过互联网访问。太棒了!第二步需要解释一下。处理微前端时的一个问题是“我该如何开发这些东西”?毕竟,我们手里只有一个大型应用程序的一个模块。如果我们想研究这些模块之间的交互怎么办?如果我们想看看我们的风格是否适合更大的用户体验怎么办?

所有这些问题的答案都可以在原生移动应用的开发中找到:我们并非在真空中开发。相反,我们有一个模拟器——一个外观和行为都与我们将要部署到的系统完全一样的软件。用微前端的术语来说,我们需要应用外壳 (App Shell) 来支持我们的开发过程。但如何做到这一点呢?尤其考虑到我们还希望在离线状态下继续开发。因此,我们需要一种共享应用外壳的方法,以实现“模拟”,从而支持快速的开发流程。

Pilet 的解剖

虽然 App Shell 固然重要,但更重要的是所有 Pilet。大多数情况下,基于 Piral 的 App Shell 处于维护模式——所有功能都是以 Pilet 的形式独立开发的。

Pilet 只是一个 NPM 包,其中包含一个 JavaScript 文件(“主包”,以 UMD 格式生成)。此外,它还可能包含其他资源(例如 CSS 文件、图片等)以及更多 JavaScript 文件(“侧包”)。

pilet 的内容

从编码角度来看,pilet 只有一个约束——它导出一个名为 的函数setup。该函数接收 API,允许 pilet 的开发人员决定在模块中使用哪些技术和功能。

简而言之,pilet 可能很简单:

import * as React from "react";
import { PiletApi } from "app-shell";

export function setup(app: PiletApi) {
  app.registerPage("/sample", () => (
    <div>
      <h1>Hello World!</h1>
      <p>Welcome to your personal pilet :-).</p>
    </div>
  ));
}

当然,桩基应该尽可能地“懒惰”。因此,任何较大的部件(甚至是可能不需要立即使用的部件)都应该只在需要时才装载。

使用我们标准工具带中的方法进行简单的转换可以帮助:

// index.tsx
import * as React from "react";
import { PiletApi } from "app-shell";

const Page = React.lazy(() => import("./Page"));

export function setup(app: PiletApi) {
  app.registerPage("/sample", Page);
}

// Page.tsx
import * as React from "react";

export default () => (
  <div>
    <h1>Hello World!</h1>
    <p>Welcome to your personal pilet :-).</p>
  </div>
);

所有这些在Piral中都能正常工作。需要注意的是,在上面(当然,相当简单的)代码库中,Piral 仅在根模块中被提及。这是一个良好且理想的设计。作为 Pilet 的作者,可以选择 Piral 的集成深度。我们建议仅使用根模块进行此集成。

到目前为止一切顺利,但是如何将 Pilet 引入我们(真实的,即已部署的)App Shell 呢?答案是 Feed 服务。我们已经看到,App Shell 从“ https://feed.piral.io/api/v1/pilet/mife-demo ”获取了一些数据。此请求的响应包含一些元数据,允许 Piral 通过接收指向其主 bundle 的链接来检索不同的 Pilet。

每个人都可以自由开发或部署定制的 feed 服务。我们提供规范和基于 Express 的 Node.js 示例,我们认为基础已经扎实。此外,我们还提供灵活的在线 feed 服务。这项服务包含高效入门所需的一切。

Piral CLI

到目前为止发生的所有神奇事情都可以在 Piral CLI 中找到。Piral CLI 是一个简单的命令行工具,可以处理以下操作:

  • 脚手架(用于piral new新的应用程序外壳或pilet new新的 Pilet)
  • 调试(用于piral debug调试应用程序外壳;供 Pilet 使用pilet debug
  • 建筑(带有piral buildpilet build
  • 发布一个 Pilet(pilet publish

在整个高层架构中,Piral CLI 的位置恰好位于开发人员和信息流服务之间。如前所述,信息流服务是此架构中唯一必需的后端组件。它将应用程序外壳与特定模块解耦,并支持更高级的用例,例如用户自定义模块交付。

Piral CLI 的职责

Piral CLI 内部使用 Parcel。因此,所有 Parcel 插件(以及它们的配置 - 如果需要)都可以正常工作。

Piral CLI 本身也支持插件。

进一步阅读

已经有一些关于 Piral 的文章。

此外,文档也可能有所帮助。它包含所有类型的见解、教程故事情节以及可用扩展的列表。

获取 Piral!

如果您正在考虑采用微前端,Piral或许是您的理想之选。它只需极少的基础设施,即可为您的用户带来最大的价值。Piral 旨在提供一流的开发体验,并支持逐步采用(例如,从现有应用程序入手,在实际开发 Pilet 之前引入加载 Pilet 的功能)。

通过可选的“转换器”(例如 Angular、Vue),可以支持多种技术或旧版技术的迁移。所有官方扩展(包括转换器)的最新列表可在我们的文档页面访问。

我们很乐意收到您的反馈!🍻

分享链接,为项目加星标⭐——非常感谢❤️!

文章来源:https://dev.to/florianrappl/microfrontends-based-on-react-4oo9
PREV
现代侧边栏菜单
NEXT
我希望在开始 Web 开发之前了解的事情