1660029384
三年多一点前,我在 LogRocket 的一篇博文中揭示了通过某种发现机制形成微前端的想法,驯服前端单体。发现机制(称为提要服务)是我参与的解决方案的核心。
那个解决方案就是 Piral,我们在半年后的 O'Reilly 的软件架构会议上正式披露了它。
今天,Piral 是微前端领域最常用和知名的解决方案之一。仅此一项就可以证明另一篇博客文章的合理性——但是,我们也有越来越多的微前端流行,以及对可扩展性的总体需求。
事不宜迟,让我们看看微前端是什么,为什么松散耦合对它们如此重要,以及 Piral 如何解决这个(和其他问题)以实现出色的可扩展性。我们将在以下部分中介绍这一点:
近年来,微前端越来越流行。原因之一是对大型 Web 应用程序的需求增加。如今,AWS 和 Azure 门户等功能强大的门户以及 Netflix 或 DAZN 等丰富的用户体验并非例外,而是常态。如何构建如此庞大的应用程序?如何缩放它们?
这些问题的一个答案可以是使用微前端。微前端是业务子域的技术表示。基本思想是单独开发一部分 UI。这件作品不需要在屏幕上的一个区域呈现;它实际上可以由多个片段组成,即一个菜单项和该菜单项链接到的页面。唯一的限制是该部分应与业务子域相关。
微前端由不同的组件组成,但这些不是经典的 UI 组件,如下拉菜单或富文本字段框。相反,这些组件是特定于域的组件,并包含一些业务逻辑,例如需要发出哪些 API 请求。
即使是像菜单项这样简单的东西在这个上下文中也是一个域组件,因为它已经知道到页面的链接来自同一个业务域。一旦组件具有一些领域逻辑,它就是一个领域组件——因此可以成为微前端的一部分。
为了实现微前端,存在一整套方法和实践。它们可以在构建时、服务器端和客户端组合在一起。
在本文中,我们将研究客户端的组合,但可以为服务器编写相同的故事。那么微前端实际上是如何扩展的呢?
许多微前端框架在现实环境中面临可扩展性问题。看其他文章,乍一看技术还不错;例如,如果您阅读使用 single-spa 创建微前端应用程序或使用 Podium 构建 Svelte 微前端,它们很好地介绍了技术和用例。另一个例子可以在使用 Fronts 构建渐进式微前端中看到。
问题是这些框架通常会尝试在视觉上分割 UI。但是,实际上,您永远不会将前端拆分为“导航”、“页眉”、“内容”和“页脚”等部分。这是为什么?
如上一节所述,一个真正的应用程序由来自不同子域的不同部分组成,这些子域组合在一起形成完整的应用程序域。虽然这些子域可以在纸上很好地完全分开,但它们通常在相同的布局元素中出现在最终用户面前。
想想像网上商店这样的东西。如果您有一个用于产品详细信息的子域和另一个处理先前订单的子域,那么您不希望在作为用户的订单历史记录中只看到无意义的产品 ID。相反,您希望订单历史记录中至少显示产品名称和一些详细信息。因此,这些子域在视觉上向最终用户交错。
同样,几乎每个子域都可以为共享的 UI 布局元素做出贡献,例如导航、页眉或页脚。因此,拥有专门处理导航区域的微前端在实践中没有多大意义,因为这个微前端会收到来自其他团队的大量请求——并成为瓶颈。这样做会产生一个隐藏的单体。
现在,有人可能会争辩说,在微前端中没有导航会导致对更改的相同需求,但这次是在应用程序外壳所有者上。这会更糟。
那么解决方案是什么呢?显然,我们需要将这些东西解耦。所以不要使用类似的东西:
import MyMenuItem1 from 'my-micro-frontend1';
import MyMenuItem1 from 'my-micro-frontend2';
import MyMenuItemN from 'my-micro-frontendN';
const MyMenu = () => (
<>
<MyMenuItem1 />
<MyMenuItem2 />
<MyMenuItemN />
</>
);
我们需要注册每个必要的部分,例如来自微前端本身的导航项。这样,我们最终可以得到如下结构:
const MyMenu = () => {
const items = useRegisteredMenuItems();
return (
<>
{items.map(({ id, Component }) => <Component key={id} />)}
</>
);
};
为了避免需要知道微前端的名称和位置,需要一种发现。这只是一个可以从已知位置(例如后端服务)检索的 JSON 文档。
现在我们知道我们需要扩展什么,是时候开始实施了。幸运的是,有一个框架可以让我们在这方面领先一步:Piral。
Piral是一个使用微前端创建超可扩展 Web 应用程序的框架。在许多方面,它具有以下特点:
这样,各个团队可以专注于他们特定的领域问题,而不是需要对齐和联合发布。Piral 应用程序由三个部分组成:
整个设置可以绘制如下:
模块开发人员可以使用命令行实用程序piral-cli
来搭建(即,使用一些模板创建)和发布新的 pilet,调试和更新现有模块,或者执行一些 linting 和验证。真正的用户不会将解决方案视为不同的部分——他们实际上是在一个应用程序中将应用程序外壳与堆砌一起使用。从技术上讲,这些桩是从饲料服务中获取的。
很多时候,微前端的开发体验并不理想。要么需要检查并开始多件事情,要么整个过程归结为一个开发-提交-尝试-失败-重启周期。Piral 在这里有所不同——它首先尝试离线。微前端直接在称为模拟器的应用程序外壳的特殊版本中开发。
模拟器只是一个 npm 包,可以安装在任何 npm 项目中。当piral-cli
用于调试时,它实际上会将模拟器中的内容作为页面来显示。Pilet 将通过内部 API 提供服务,而不是使用远程提要服务或类似的东西。
尽管如此,在开发过程中加载现有的微前端可能仍然有意义。在这种情况下,仍然可以集成来自现有饲料的桩。
让我们看看这一切在实践中是如何运作的。
有多种方法可以使用 Piral 创建应用程序外壳:
piral-cli
创建新项目在这篇文章中,我们将做后者。
在命令行上,我们运行:
npm init piral-instance --bundler esbuild --target my-app-shell --defaults
my-app-shell
这将在目录中创建一个新的应用程序外壳。该项目将使用 npm、TypeScript 和esbuild
工具作为我们的捆绑器(尽管我们实际上可以选择任何类型的捆绑器,例如 webpack、Parcel 或 Vite 等)。在许多情况下,选择esbuild
应该就足够了,并且可以提供最快的安装时间。
现在,我们可以开始调试项目了。进入新目录(例如,cd my-app-shell
)并开始调试会话:
npm start
Going tohttp://localhost:1234
应该向您展示标准模板:
我们现在可以在所有可能的方面更改模板。例如,我们可以将提供的布局更改为没有任何固定的内容图块;只需编辑src/layout.tsx
文件并删除defaultTiles
和defaultMenuItems
. 确保不仅要删除它们的初始化,还要删除对它们的引用。
要获得更详细的信息,我们可以更改DashboardContainer
from:
DashboardContainer: ({ children }) => (
<div>
<h1>Hello, world!</h1>
<p>Welcome to your new microfrontend app shell, built with:</p>
<div className="tiles">
{defaultTiles}
{children}
</div>
</div>
),
至:
DashboardContainer: ({ children }) => (
<div>
<h1>Hello, world!</h1>
<p>Welcome to your new microfrontend app shell, built with:</p>
<div className="tiles">
{children}
</div>
</div>
),
这里可以看到的所有组件都有不同的用途。虽然其中许多来自可选插件,但有些——例如ErrorInfo
or Layout
——已经通过驱动 Piral 的核心库定义。
在上面的例子中,我们为 Piral 的仪表板插件定义了仪表板容器。仪表板插件为我们提供了一个仪表板,默认情况下,它位于/
我们页面的主页 ( )。我们可以在这里更改所有内容,包括它的外观和位置(当然,如果我们想要一个仪表板)。
仪表板非常适合门户应用程序,因为它们在一个屏幕上收集大量信息。对于微前端,仪表板也很好——尤其是作为展示。在这个页面上,可能大多数(如果不是全部)微前端想要展示一些东西。
从仪表板容器中删除默认磁贴后,Web 应用程序现在应该看起来更空:
我们的 Web 应用程序空虚的主要原因是我们没有集成任何在某处呈现组件的微前端。此时,脚手架机制将我们的新应用程序外壳连接到 Piral 本身拥有的特殊提要:空提要。
空提要是一种提要,顾名思义,它将始终保持为空。我们可以更改应用程序外壳的代码以转到其他提要。
为此,您需要打开src/index.tsx
文件。在那里,您将看到带有要使用的提要的 URL 的变量:
const feedUrl = 'https://feed.piral.cloud/api/v1/pilet/sample';
转到在另一个应用程序 shell中使用的示例提要,我们实际上可以看到如果由微前端正确填充 shell 的外观。仪表板现在应该如下所示:
我们已经可以在我们的新应用程序外壳中显示来自另一个提要的堆,这一事实实际上非常酷。这预先表明桩是真正独立的,并且与它们的外壳没有很强的关系。
但是,请记住,这种平滑的集成可能并不总是可行的。Pilets 总是可以以一种相当容易集成到其他 Piral 实例中的方式开发。同样,也可以以排除不同应用程序外壳的方式开发 pilet。
在我们为这个应用程序外壳开发一些微前端之前,我们需要创建它的模拟器。停止调试过程并运行以下命令:
npm run build
这将piral build
在当前项目上运行。结果是在 中有两个子目录dist
:
dist/release
dist/emulator
虽然前者可用于在某处实际部署我们的 Web 应用程序,但后者包含一个.tgz
可以上传到注册表的文件,如下所示:
npm publish dist/emulator/my-app-shell-1.0.0.tgz
您可能需要 npm 凭据来发布包,但即使您已经登录到 npm,您也可能不想发布它,而是将其保密,或者在不同的注册表上发布。
要使用自定义注册表测试发布过程,您可以使用Verdaccio。在一个新的 shell 中,启动:
npx verdaccio
这将安装并运行 Verdaccio 的本地版本。您应该会在屏幕上看到类似以下内容:
warn --- http address - http://localhost:4873/ - verdaccio/5.13.1
转到此地址并查看说明。它们应该看起来像:
运行登录命令 ( npm adduser --registry
http://localhost:4873/
) 并填写数据。对于Username
and Password
,您可以只使用test
. 什么都会被拿走;可以Email
很简单foo@bar.com
。
现在可以通过以下方式发布到自定义注册表:
npm publish dist/emulator/my-app-shell-1.0.0.tgz --registry http://localhost:4873/
完成后,我们可以为这个 shell 创建微前端!
正如我们对 app shell 所做的那样,我们可以使用piral-cli
来搭建一个项目。这个命令现在使用pilet
而不是piral-instance
. 我们跑:
npm init pilet --source my-app-shell --registry http://localhost:4873/ --bundler esbuild --target my-pilet --defaults
这将创建一个名为的新目录my-pilet
,其中包含新微前端的代码。工具设置为esbuild
(像以前一样,我们使用 esbuild,因为它安装得非常快,但你也可以选择不同的东西,比如 webpack)。
上面的重要部分是指定--source
,它表示用于开发的模拟器。现在一切就绪,我们可以cd my-pilet
运行:
npm start
和以前一样,开发服务器托管在http://localhost:1234
. 转到那里会产生如下所示的页面:
几乎就像我们使用了empty
提要一样空。但是,在这种情况下,新 pilet 的模板已经注册了一个 tile 和一个菜单项。让我们看看如何改变这一点。
打开src/index.tsx
文件并查看代码:
import * as React from 'react';
import type { PiletApi } from 'my-app-shell';
export function setup(api: PiletApi) {
api.showNotification('Hello from Piral!', {
autoClose: 2000,
});
api.registerMenu(() =>
<a href="https://docs.piral.io" target="_blank">Documentation</a>
);
api.registerTile(() => <div>Welcome to Piral!</div>, {
initialColumns: 2,
initialRows: 1,
});
}
简单来说,pilet 就是一个 JavaScript 库;重要的部分是这个库导出的内容。
Pilet 导出一个setup
函数(准确地说,还可以导出一个函数teardown
)。该函数在微前端连接后使用,并接收单个参数 ,api
该参数由应用程序外壳定义和创建。
app shell 的 API(通常称为 Pilet API)是 pilet 可以在应用程序中注册其部件的地方。让我们添加一个页面并稍微更改一下磁贴。
我们从瓷砖开始。我们可以给它一些类,比如teaser
实际上有一点背景。此外,我们想为仪表板容器添加一些元数据。我们可以使用initialColumns
和initialRows
属性来传达所需的大小。
app.registerTile(() => <div className="teaser">Hello LogRocket!</div>, {
initialColumns: 2,
initialRows: 2,
})
保存后,该图块将具有一些不同的外观。让我们删除showNotification
不再需要的并引入一个新页面:
api.registerPage('/foo', () =>
<p>This is my page</p>
);
要链接到此页面,我们可以更改已注册的菜单项。要执行 SPA 导航,我们可以使用熟悉的 React 工具react-router-dom
:
api.registerMenu(() =>
<Link to="/foo">Foo</Link>
);
伟大的!然而,像页面这样的片段并不总是必需的,并且应该只在应该呈现它们时加载。这种延迟加载可以通过将代码放在一个专用文件中来实现,即,Page.tsx
并将注册更改为:
const Page = React.lazy(() => import('./Page'));
api.registerPage('/foo', Page);
的内容Page.tsx
可以很简单:
import * as React from 'react';
export default () => {
return (
<>
<h1>Page Title</h1>
<p>Lorem ipsum dolor sit ...</p>
<p>Lorem ipsum dolor sit ...</p>
</>
);
};
注册页面后,您现在可以单击导航栏中的“Foo”并查看页面:
现在我们的pilet已经写好了,我们可以实际构建和发布它了。在这一点上,我们还没有创建自己的提要或在任何地方发布应用程序外壳,所以最后一部分实际上有点理论。
要构建 pilet,您可以运行:
npm run build
创建后,您可以使用npx pilet pack
. 这将与运行非常相似npm pack
。结果是另一个.tgz
文件——这次不是模拟器,而是实际的 pilet。tarball 是可以上传到专用服务(例如提要服务)的内容,提供应用程序外壳使用的提要。
可以在piral.cloud上找到非商业和商业产品的示例。
在结束本教程之前,让我们看看如何集成一个常用功能——在本例中,使用 SWR 执行 HTTP 请求。
有多种方法可以集成常见的问题,例如 SWR。将 swr(或位于其之上的其他库)添加到应用程序 shell 并在那里进行配置后,您有三个选项:
swr
留给 pilet:它们可以swr
作为分布式依赖项共享(即,仅在没有其他 pilet 加载它时才加载它)集成 SWT 最简单和最可靠的方法是使用第一个选项。为此,我们回到应用程序外壳。
在应用程序外壳的目录中运行:
npm install swr
现在,让我们修改package.json
. 保留几乎所有内容,但修改该部分的externals
数组,pilets
如下所示:
{
"name": "my-app-shell",
"version": "1.1.0",
// ...
"pilets": {
"externals": ["swr"],
// ...
},
// ...
}
请注意,我还更改了版本号。由于我们将对模拟器进行更新,因此我们需要一个新版本。这将指示 Piralswr
与所有 pilet 共享依赖项。
为了实际测试这一点,让我们再次编写npm run build
和发布。
npm run build
npm publish dist/emulator/my-app-shell-1.1.0.tgz --registry http://localhost:4873/
有了更新的 shell,让我们进入 pilet 的目录并升级应用程序 shell:
npx pilet upgrade
桩的package.json
文件应该已经改变。它现在应该包含对my-app-shell
in version1.1.0
而不是1.0.0
. 此外,您应该会看到和中swr
列出的内容。devDependenciespeerDependencies
让我们修改要使用的页面swr
:
import * as React from 'react';
import LogRocket from 'swr';
// note: fetcher could have also been globally configured in the app shell
// however, in general the pilet's don't know about this and so they may want to
// reconfigure or redefine it like here
const fetcher = (resource, init) => fetch(resource, init).then(res => res.json());
export default () => {
const { data, error } = useSWR('https://jsonplaceholder.typicode.com/users/1', fetcher);
if (error) {
return <div>failed to load</div>;
}
if (!data) {
return <div>loading...</div>;
}
return (
<>
Hello {data.name}!
</>
);
};
现在我们完成了!不仅在我们的应用程序中成功设置了 SWR,我们还可以在所有微前端中使用它。这既节省了加载 SWR 的带宽,也节省了 SWR 的内部缓存,为所有微前端带来了不错的性能优势。
在这篇文章中,您已经看到了 Piral 入门是多么容易。Piral 为您提供了将 Web 应用程序分发到不同存储库的选项,甚至跨不同的团队。
我们在这篇文章中只探讨了非常基本的设置,但您可以使用 Piral 做更多的事情。探索 Piral 的最佳方式是阅读官方文档。
Piral 比大多数其他解决方案具有更好的扩展性的原因是 Piral 鼓励松散耦合。这样,您将很难将两个东西融合在一起,这有助于您避免功能重叠和隐藏的单体。
无论您打算做什么,请确保已经考虑了要共享哪些依赖项以及要保留哪些依赖项。我们已经看到了一个示例,其中提供swr
作为共享依赖项实际上是在几秒钟内设置的。快乐编码!
来源:https ://blog.logrocket.com/creating-micro-frontends-piral/
1660029384
三年多一点前,我在 LogRocket 的一篇博文中揭示了通过某种发现机制形成微前端的想法,驯服前端单体。发现机制(称为提要服务)是我参与的解决方案的核心。
那个解决方案就是 Piral,我们在半年后的 O'Reilly 的软件架构会议上正式披露了它。
今天,Piral 是微前端领域最常用和知名的解决方案之一。仅此一项就可以证明另一篇博客文章的合理性——但是,我们也有越来越多的微前端流行,以及对可扩展性的总体需求。
事不宜迟,让我们看看微前端是什么,为什么松散耦合对它们如此重要,以及 Piral 如何解决这个(和其他问题)以实现出色的可扩展性。我们将在以下部分中介绍这一点:
近年来,微前端越来越流行。原因之一是对大型 Web 应用程序的需求增加。如今,AWS 和 Azure 门户等功能强大的门户以及 Netflix 或 DAZN 等丰富的用户体验并非例外,而是常态。如何构建如此庞大的应用程序?如何缩放它们?
这些问题的一个答案可以是使用微前端。微前端是业务子域的技术表示。基本思想是单独开发一部分 UI。这件作品不需要在屏幕上的一个区域呈现;它实际上可以由多个片段组成,即一个菜单项和该菜单项链接到的页面。唯一的限制是该部分应与业务子域相关。
微前端由不同的组件组成,但这些不是经典的 UI 组件,如下拉菜单或富文本字段框。相反,这些组件是特定于域的组件,并包含一些业务逻辑,例如需要发出哪些 API 请求。
即使是像菜单项这样简单的东西在这个上下文中也是一个域组件,因为它已经知道到页面的链接来自同一个业务域。一旦组件具有一些领域逻辑,它就是一个领域组件——因此可以成为微前端的一部分。
为了实现微前端,存在一整套方法和实践。它们可以在构建时、服务器端和客户端组合在一起。
在本文中,我们将研究客户端的组合,但可以为服务器编写相同的故事。那么微前端实际上是如何扩展的呢?
许多微前端框架在现实环境中面临可扩展性问题。看其他文章,乍一看技术还不错;例如,如果您阅读使用 single-spa 创建微前端应用程序或使用 Podium 构建 Svelte 微前端,它们很好地介绍了技术和用例。另一个例子可以在使用 Fronts 构建渐进式微前端中看到。
问题是这些框架通常会尝试在视觉上分割 UI。但是,实际上,您永远不会将前端拆分为“导航”、“页眉”、“内容”和“页脚”等部分。这是为什么?
如上一节所述,一个真正的应用程序由来自不同子域的不同部分组成,这些子域组合在一起形成完整的应用程序域。虽然这些子域可以在纸上很好地完全分开,但它们通常在相同的布局元素中出现在最终用户面前。
想想像网上商店这样的东西。如果您有一个用于产品详细信息的子域和另一个处理先前订单的子域,那么您不希望在作为用户的订单历史记录中只看到无意义的产品 ID。相反,您希望订单历史记录中至少显示产品名称和一些详细信息。因此,这些子域在视觉上向最终用户交错。
同样,几乎每个子域都可以为共享的 UI 布局元素做出贡献,例如导航、页眉或页脚。因此,拥有专门处理导航区域的微前端在实践中没有多大意义,因为这个微前端会收到来自其他团队的大量请求——并成为瓶颈。这样做会产生一个隐藏的单体。
现在,有人可能会争辩说,在微前端中没有导航会导致对更改的相同需求,但这次是在应用程序外壳所有者上。这会更糟。
那么解决方案是什么呢?显然,我们需要将这些东西解耦。所以不要使用类似的东西:
import MyMenuItem1 from 'my-micro-frontend1';
import MyMenuItem1 from 'my-micro-frontend2';
import MyMenuItemN from 'my-micro-frontendN';
const MyMenu = () => (
<>
<MyMenuItem1 />
<MyMenuItem2 />
<MyMenuItemN />
</>
);
我们需要注册每个必要的部分,例如来自微前端本身的导航项。这样,我们最终可以得到如下结构:
const MyMenu = () => {
const items = useRegisteredMenuItems();
return (
<>
{items.map(({ id, Component }) => <Component key={id} />)}
</>
);
};
为了避免需要知道微前端的名称和位置,需要一种发现。这只是一个可以从已知位置(例如后端服务)检索的 JSON 文档。
现在我们知道我们需要扩展什么,是时候开始实施了。幸运的是,有一个框架可以让我们在这方面领先一步:Piral。
Piral是一个使用微前端创建超可扩展 Web 应用程序的框架。在许多方面,它具有以下特点:
这样,各个团队可以专注于他们特定的领域问题,而不是需要对齐和联合发布。Piral 应用程序由三个部分组成:
整个设置可以绘制如下:
模块开发人员可以使用命令行实用程序piral-cli
来搭建(即,使用一些模板创建)和发布新的 pilet,调试和更新现有模块,或者执行一些 linting 和验证。真正的用户不会将解决方案视为不同的部分——他们实际上是在一个应用程序中将应用程序外壳与堆砌一起使用。从技术上讲,这些桩是从饲料服务中获取的。
很多时候,微前端的开发体验并不理想。要么需要检查并开始多件事情,要么整个过程归结为一个开发-提交-尝试-失败-重启周期。Piral 在这里有所不同——它首先尝试离线。微前端直接在称为模拟器的应用程序外壳的特殊版本中开发。
模拟器只是一个 npm 包,可以安装在任何 npm 项目中。当piral-cli
用于调试时,它实际上会将模拟器中的内容作为页面来显示。Pilet 将通过内部 API 提供服务,而不是使用远程提要服务或类似的东西。
尽管如此,在开发过程中加载现有的微前端可能仍然有意义。在这种情况下,仍然可以集成来自现有饲料的桩。
让我们看看这一切在实践中是如何运作的。
有多种方法可以使用 Piral 创建应用程序外壳:
piral-cli
创建新项目在这篇文章中,我们将做后者。
在命令行上,我们运行:
npm init piral-instance --bundler esbuild --target my-app-shell --defaults
my-app-shell
这将在目录中创建一个新的应用程序外壳。该项目将使用 npm、TypeScript 和esbuild
工具作为我们的捆绑器(尽管我们实际上可以选择任何类型的捆绑器,例如 webpack、Parcel 或 Vite 等)。在许多情况下,选择esbuild
应该就足够了,并且可以提供最快的安装时间。
现在,我们可以开始调试项目了。进入新目录(例如,cd my-app-shell
)并开始调试会话:
npm start
Going tohttp://localhost:1234
应该向您展示标准模板:
我们现在可以在所有可能的方面更改模板。例如,我们可以将提供的布局更改为没有任何固定的内容图块;只需编辑src/layout.tsx
文件并删除defaultTiles
和defaultMenuItems
. 确保不仅要删除它们的初始化,还要删除对它们的引用。
要获得更详细的信息,我们可以更改DashboardContainer
from:
DashboardContainer: ({ children }) => (
<div>
<h1>Hello, world!</h1>
<p>Welcome to your new microfrontend app shell, built with:</p>
<div className="tiles">
{defaultTiles}
{children}
</div>
</div>
),
至:
DashboardContainer: ({ children }) => (
<div>
<h1>Hello, world!</h1>
<p>Welcome to your new microfrontend app shell, built with:</p>
<div className="tiles">
{children}
</div>
</div>
),
这里可以看到的所有组件都有不同的用途。虽然其中许多来自可选插件,但有些——例如ErrorInfo
or Layout
——已经通过驱动 Piral 的核心库定义。
在上面的例子中,我们为 Piral 的仪表板插件定义了仪表板容器。仪表板插件为我们提供了一个仪表板,默认情况下,它位于/
我们页面的主页 ( )。我们可以在这里更改所有内容,包括它的外观和位置(当然,如果我们想要一个仪表板)。
仪表板非常适合门户应用程序,因为它们在一个屏幕上收集大量信息。对于微前端,仪表板也很好——尤其是作为展示。在这个页面上,可能大多数(如果不是全部)微前端想要展示一些东西。
从仪表板容器中删除默认磁贴后,Web 应用程序现在应该看起来更空:
我们的 Web 应用程序空虚的主要原因是我们没有集成任何在某处呈现组件的微前端。此时,脚手架机制将我们的新应用程序外壳连接到 Piral 本身拥有的特殊提要:空提要。
空提要是一种提要,顾名思义,它将始终保持为空。我们可以更改应用程序外壳的代码以转到其他提要。
为此,您需要打开src/index.tsx
文件。在那里,您将看到带有要使用的提要的 URL 的变量:
const feedUrl = 'https://feed.piral.cloud/api/v1/pilet/sample';
转到在另一个应用程序 shell中使用的示例提要,我们实际上可以看到如果由微前端正确填充 shell 的外观。仪表板现在应该如下所示:
我们已经可以在我们的新应用程序外壳中显示来自另一个提要的堆,这一事实实际上非常酷。这预先表明桩是真正独立的,并且与它们的外壳没有很强的关系。
但是,请记住,这种平滑的集成可能并不总是可行的。Pilets 总是可以以一种相当容易集成到其他 Piral 实例中的方式开发。同样,也可以以排除不同应用程序外壳的方式开发 pilet。
在我们为这个应用程序外壳开发一些微前端之前,我们需要创建它的模拟器。停止调试过程并运行以下命令:
npm run build
这将piral build
在当前项目上运行。结果是在 中有两个子目录dist
:
dist/release
dist/emulator
虽然前者可用于在某处实际部署我们的 Web 应用程序,但后者包含一个.tgz
可以上传到注册表的文件,如下所示:
npm publish dist/emulator/my-app-shell-1.0.0.tgz
您可能需要 npm 凭据来发布包,但即使您已经登录到 npm,您也可能不想发布它,而是将其保密,或者在不同的注册表上发布。
要使用自定义注册表测试发布过程,您可以使用Verdaccio。在一个新的 shell 中,启动:
npx verdaccio
这将安装并运行 Verdaccio 的本地版本。您应该会在屏幕上看到类似以下内容:
warn --- http address - http://localhost:4873/ - verdaccio/5.13.1
转到此地址并查看说明。它们应该看起来像:
运行登录命令 ( npm adduser --registry
http://localhost:4873/
) 并填写数据。对于Username
and Password
,您可以只使用test
. 什么都会被拿走;可以Email
很简单foo@bar.com
。
现在可以通过以下方式发布到自定义注册表:
npm publish dist/emulator/my-app-shell-1.0.0.tgz --registry http://localhost:4873/
完成后,我们可以为这个 shell 创建微前端!
正如我们对 app shell 所做的那样,我们可以使用piral-cli
来搭建一个项目。这个命令现在使用pilet
而不是piral-instance
. 我们跑:
npm init pilet --source my-app-shell --registry http://localhost:4873/ --bundler esbuild --target my-pilet --defaults
这将创建一个名为的新目录my-pilet
,其中包含新微前端的代码。工具设置为esbuild
(像以前一样,我们使用 esbuild,因为它安装得非常快,但你也可以选择不同的东西,比如 webpack)。
上面的重要部分是指定--source
,它表示用于开发的模拟器。现在一切就绪,我们可以cd my-pilet
运行:
npm start
和以前一样,开发服务器托管在http://localhost:1234
. 转到那里会产生如下所示的页面:
几乎就像我们使用了empty
提要一样空。但是,在这种情况下,新 pilet 的模板已经注册了一个 tile 和一个菜单项。让我们看看如何改变这一点。
打开src/index.tsx
文件并查看代码:
import * as React from 'react';
import type { PiletApi } from 'my-app-shell';
export function setup(api: PiletApi) {
api.showNotification('Hello from Piral!', {
autoClose: 2000,
});
api.registerMenu(() =>
<a href="https://docs.piral.io" target="_blank">Documentation</a>
);
api.registerTile(() => <div>Welcome to Piral!</div>, {
initialColumns: 2,
initialRows: 1,
});
}
简单来说,pilet 就是一个 JavaScript 库;重要的部分是这个库导出的内容。
Pilet 导出一个setup
函数(准确地说,还可以导出一个函数teardown
)。该函数在微前端连接后使用,并接收单个参数 ,api
该参数由应用程序外壳定义和创建。
app shell 的 API(通常称为 Pilet API)是 pilet 可以在应用程序中注册其部件的地方。让我们添加一个页面并稍微更改一下磁贴。
我们从瓷砖开始。我们可以给它一些类,比如teaser
实际上有一点背景。此外,我们想为仪表板容器添加一些元数据。我们可以使用initialColumns
和initialRows
属性来传达所需的大小。
app.registerTile(() => <div className="teaser">Hello LogRocket!</div>, {
initialColumns: 2,
initialRows: 2,
})
保存后,该图块将具有一些不同的外观。让我们删除showNotification
不再需要的并引入一个新页面:
api.registerPage('/foo', () =>
<p>This is my page</p>
);
要链接到此页面,我们可以更改已注册的菜单项。要执行 SPA 导航,我们可以使用熟悉的 React 工具react-router-dom
:
api.registerMenu(() =>
<Link to="/foo">Foo</Link>
);
伟大的!然而,像页面这样的片段并不总是必需的,并且应该只在应该呈现它们时加载。这种延迟加载可以通过将代码放在一个专用文件中来实现,即,Page.tsx
并将注册更改为:
const Page = React.lazy(() => import('./Page'));
api.registerPage('/foo', Page);
的内容Page.tsx
可以很简单:
import * as React from 'react';
export default () => {
return (
<>
<h1>Page Title</h1>
<p>Lorem ipsum dolor sit ...</p>
<p>Lorem ipsum dolor sit ...</p>
</>
);
};
注册页面后,您现在可以单击导航栏中的“Foo”并查看页面:
现在我们的pilet已经写好了,我们可以实际构建和发布它了。在这一点上,我们还没有创建自己的提要或在任何地方发布应用程序外壳,所以最后一部分实际上有点理论。
要构建 pilet,您可以运行:
npm run build
创建后,您可以使用npx pilet pack
. 这将与运行非常相似npm pack
。结果是另一个.tgz
文件——这次不是模拟器,而是实际的 pilet。tarball 是可以上传到专用服务(例如提要服务)的内容,提供应用程序外壳使用的提要。
可以在piral.cloud上找到非商业和商业产品的示例。
在结束本教程之前,让我们看看如何集成一个常用功能——在本例中,使用 SWR 执行 HTTP 请求。
有多种方法可以集成常见的问题,例如 SWR。将 swr(或位于其之上的其他库)添加到应用程序 shell 并在那里进行配置后,您有三个选项:
swr
留给 pilet:它们可以swr
作为分布式依赖项共享(即,仅在没有其他 pilet 加载它时才加载它)集成 SWT 最简单和最可靠的方法是使用第一个选项。为此,我们回到应用程序外壳。
在应用程序外壳的目录中运行:
npm install swr
现在,让我们修改package.json
. 保留几乎所有内容,但修改该部分的externals
数组,pilets
如下所示:
{
"name": "my-app-shell",
"version": "1.1.0",
// ...
"pilets": {
"externals": ["swr"],
// ...
},
// ...
}
请注意,我还更改了版本号。由于我们将对模拟器进行更新,因此我们需要一个新版本。这将指示 Piralswr
与所有 pilet 共享依赖项。
为了实际测试这一点,让我们再次编写npm run build
和发布。
npm run build
npm publish dist/emulator/my-app-shell-1.1.0.tgz --registry http://localhost:4873/
有了更新的 shell,让我们进入 pilet 的目录并升级应用程序 shell:
npx pilet upgrade
桩的package.json
文件应该已经改变。它现在应该包含对my-app-shell
in version1.1.0
而不是1.0.0
. 此外,您应该会看到和中swr
列出的内容。devDependenciespeerDependencies
让我们修改要使用的页面swr
:
import * as React from 'react';
import LogRocket from 'swr';
// note: fetcher could have also been globally configured in the app shell
// however, in general the pilet's don't know about this and so they may want to
// reconfigure or redefine it like here
const fetcher = (resource, init) => fetch(resource, init).then(res => res.json());
export default () => {
const { data, error } = useSWR('https://jsonplaceholder.typicode.com/users/1', fetcher);
if (error) {
return <div>failed to load</div>;
}
if (!data) {
return <div>loading...</div>;
}
return (
<>
Hello {data.name}!
</>
);
};
现在我们完成了!不仅在我们的应用程序中成功设置了 SWR,我们还可以在所有微前端中使用它。这既节省了加载 SWR 的带宽,也节省了 SWR 的内部缓存,为所有微前端带来了不错的性能优势。
在这篇文章中,您已经看到了 Piral 入门是多么容易。Piral 为您提供了将 Web 应用程序分发到不同存储库的选项,甚至跨不同的团队。
我们在这篇文章中只探讨了非常基本的设置,但您可以使用 Piral 做更多的事情。探索 Piral 的最佳方式是阅读官方文档。
Piral 比大多数其他解决方案具有更好的扩展性的原因是 Piral 鼓励松散耦合。这样,您将很难将两个东西融合在一起,这有助于您避免功能重叠和隐藏的单体。
无论您打算做什么,请确保已经考虑了要共享哪些依赖项以及要保留哪些依赖项。我们已经看到了一个示例,其中提供swr
作为共享依赖项实际上是在几秒钟内设置的。快乐编码!
来源:https ://blog.logrocket.com/creating-micro-frontends-piral/
1660021200
3 年ちょっと前、私は LogRocket の記事「フロントエンド モノリスを使いこなす」で、ある種の発見メカニズムを介してマイクロ フロントエンドを形成するというアイデアを明らかにしました。フィード サービスとして知られる検出メカニズムは、私が貢献したソリューションの中心にありました。
そのソリューションが Piral であり、半年後にオライリーのソフトウェア アーキテクチャ カンファレンスで正式に発表されました。
今日、Piral は、マイクロ フロントエンド スペースで最も使用され、よく知られているソリューションの 1 つです。それだけでも別のブログ投稿が正当化されますが、マイクロ フロントエンドの人気が高まり、スケーラビリティ全般に対する需要も高まっています。
これ以上苦労することなく、マイクロフロントエンドとは何か、なぜ疎結合が重要なのか、Piral がこれ (およびその他の問題) を解決して優れたスケーラビリティを実現する方法を見てみましょう。これについては、次のセクションで説明します。
近年、マイクロフロントエンドの人気が高まっています。この理由の 1 つは、大規模な Web アプリケーションに対する需要の増加です。今日では、AWS や Azure ポータルなどの強力なポータルと、Netflix や DAZN などの豊富なユーザー エクスペリエンスが例外ではなく標準になっています。このような大規模なアプリケーションを構築するにはどうすればよいでしょうか? どのようにスケーリングできますか?
これらの質問に対する 1 つの答えは、マイクロ フロントエンドを使用することです。マイクロ フロントエンドは、ビジネス サブドメインの技術的な表現です。基本的な考え方は、UI の一部を分離して開発することです。この部分は、画面上の 1 つの領域に表示する必要はありません。実際には複数のフラグメント、つまり、メニュー項目とメニュー項目がリンクするページで構成できます。唯一の制約は、作品がビジネス サブドメインに関連している必要があることです。
マイクロ フロントエンドはさまざまなコンポーネントで構成されていますが、これらはドロップダウンやリッチ テキスト フィールド ボックスのような従来の UI コンポーネントではありません。代わりに、これらのコンポーネントはドメイン固有のコンポーネントであり、どの API 要求を行う必要があるかなど、いくつかのビジネス ロジックが含まれています。
メニュー項目のような単純なものでさえ、このコンテキストではドメイン コンポーネントです。これは、ページへのリンクが同じビジネス ドメインからのものであることを既に認識しているためです。コンポーネントにドメイン ロジックが含まれると、それはドメイン コンポーネントになります。つまり、マイクロ フロントエンドの一部になることができます。
マイクロフロントエンドを実装するために、一連のアプローチとプラクティスが存在します。それらは、ビルド時、サーバー側、およびクライアント側でまとめることができます。
この記事では、クライアント側での構成を見ていきますが、サーバーについても同じ話が書けるかもしれません。では、マイクロフロントエンドは実際にどのようにスケーリングするのでしょうか?
多くのマイクロ フロントエンド フレームワークは、現実世界のコンテキストでスケーラビリティの問題に直面しています。他の記事を見ると、この技術は一見健全に見えます。たとえば、Creating micro-frontend apps with single-spaやBuilding Svelte micro-frontends with Podiumを読むと、テクノロジーとユースケースがよく紹介されています。別の例は、Fronts を使用してプログレッシブ マイクロ フロントエンドを構築する で見ることができます。
問題は、これらのフレームワークが通常、UI を視覚的に分割しようとすることです。ただし、実際には、フロントエンドを「ナビゲーション」、「ヘッダー」、「コンテンツ」、「フッター」などのパーツに分割することはありません。何故ですか?
実際のアプリケーションは、前のセクションで説明したように、完全なアプリケーション ドメインを形成するために集まったさまざまなサブドメインからのさまざまな部分で構成されています。これらのサブドメインは、紙の上ではうまく分離できますが、通常、エンド ユーザーには同じレイアウト要素内に表示されます。
ウェブショップのようなものを考えてください。製品の詳細用のサブドメインと、以前の注文を処理する別のサブドメインがある場合、ユーザーとして注文履歴に無意味な製品の ID だけを表示したくないでしょう。代わりに、少なくとも製品名といくつかの詳細が注文履歴に表示されることを期待します. したがって、これらのサブドメインは、エンド ユーザーに向かって視覚的にインターリーブします。
同様に、ほぼすべてのサブドメインには、ナビゲーション、ヘッダー、フッターなどの共有 UI レイアウト要素に貢献するものがあります。したがって、ナビゲーション エリアだけを処理するマイクロ フロントエンドを持つことは、実際にはあまり意味がありません。なぜなら、このマイクロ フロントエンドは他のチームから多くのリクエストを受け取り、ボトルネックになるからです。これを行うと、隠れたモノリスが発生します。
ここで、マイクロ フロントエンドにナビゲーションがない場合、変更に対する要求は同じになるが、今度はアプリ シェルの所有者に対して要求されると主張する人がいるかもしれません。これはさらに悪いことです。
それで、解決策は何ですか?明らかに、これらのものを分離する必要があります。したがって、次のようなものを使用する代わりに:
import MyMenuItem1 from 'my-micro-frontend1';
import MyMenuItem1 from 'my-micro-frontend2';
import MyMenuItemN from 'my-micro-frontendN';
const MyMenu = () => (
<>
<MyMenuItem1 />
<MyMenuItem2 />
<MyMenuItemN />
</>
);
マイクロ フロントエンド自体のナビゲーション アイテムなど、必要な各パーツを登録する必要があります。このようにして、次のような構造になる可能性があります。
const MyMenu = () => {
const items = useRegisteredMenuItems();
return (
<>
{items.map(({ id, Component }) => <Component key={id} />)}
</>
);
};
マイクロフロントエンドの名前と場所を知る必要がないようにするには、一種の発見が必要です。これは、バックエンド サービスなどの既知の場所から取得できる単なる JSON ドキュメントです。
何をスケーリングする必要があるかがわかったので、実装を開始します。幸いなことに、これに関してすでに有利なスタートを切ることができるフレームワークがあります: Piral.
Piralは、マイクロ フロントエンドを使用して超スケーラブルな Web アプリを作成するためのフレームワークです。多くの機能の中でも、次の機能があります。
このようにして、個々のチームは、調整や共同リリースを必要とする代わりに、特定のドメインの問題に集中できます。Piral アプリケーションは、次の 3 つの部分で構成されます。
全体のセットアップは、次のようにスケッチできます。
モジュール開発者は、コマンド ライン ユーティリティpiral-cli
を使用してスキャフォールディング (つまり、テンプレートを使用して作成) し、新しいピレットを公開したり、既存のモジュールをデバッグおよび更新したり、リンティングや検証を実行したりできます。実際のユーザーは、ソリューションを別々のピースとして見ていません。実際には、アプリ シェルとピレットを 1 つのアプリケーションで使用しています。技術的には、これらのピレットはフィード サービスから取得されます。
多くの場合、マイクロ フロントエンドの開発経験は理想的ではありません。複数のことをチェックアウトして開始する必要があるか、プロセス全体が開発-送信-試行-失敗-再起動のサイクルに要約されます。Piral はここで異なります — オフライン優先であることを試みます。マイクロフロントエンドは、エミュレーターと呼ばれるアプリ シェルの特別版で直接開発されます。
エミュレーターは、任意の npm プロジェクトにインストールできる単なる npm パッケージです。piral-cli
をデバッグに使用する場合、実際にはエミュレーターのコンテンツを表示するページとして使用します。ピレットは、リモート フィード サービスなどに行く代わりに、内部 API を介して提供されます。
それでも、開発中に既存のマイクロフロントエンドをロードすることは理にかなっているかもしれません。このような場合でも、既存の飼料からのピレットを統合することができます。
これが実際にどのように機能するか見てみましょう。
Piral を使用して App Shell を作成するには、いくつかの方法があります。
piral-cli
て新しいプロジェクトを作成するこの投稿では、後者を行います。
コマンドラインで、次を実行します。
npm init piral-instance --bundler esbuild --target my-app-shell --defaults
これにより、ディレクトリに新しいアプリ シェルが作成されますmy-app-shell
。プロジェクトは、npm、TypeScript、およびesbuild
ツールをバンドラーとして使用します (ただし、実際には、webpack、Parcel、または Vite など、任意の種類のバンドラーを選択できます)。多くの場合、選択esbuild
するだけで十分であり、インストール時間が最も短いという利点があります。
これで、プロジェクトのデバッグを開始できます。新しいディレクトリ (例: cd my-app-shell
) に移動し、デバッグ セッションを開始します。
npm start
に行くとhttp://localhost:1234
、標準のテンプレートが表示されます。
これで、考えられるすべての面でテンプレートを変更できます。たとえば、提供されたレイアウトを変更して、固定コンテンツ タイルを持たないようにすることができます。src/layout.tsx
ファイルを編集して と を削除するだけdefaultTiles
ですdefaultMenuItems
。それらの初期化だけでなく、それらへの参照も必ず削除してください。
より詳細な情報を取得するには、次のように変更できますDashboardContainer
。
DashboardContainer: ({ children }) => (
<div>
<h1>Hello, world!</h1>
<p>Welcome to your new microfrontend app shell, built with:</p>
<div className="tiles">
{defaultTiles}
{children}
</div>
</div>
),
に:
DashboardContainer: ({ children }) => (
<div>
<h1>Hello, world!</h1>
<p>Welcome to your new microfrontend app shell, built with:</p>
<div className="tiles">
{children}
</div>
</div>
),
ここに表示されているすべてのコンポーネントは、さまざまな目的を果たします。それらの多くはオプションのプラグインから来ていますが、ErrorInfo
orなどのいくつかLayout
は、Piral を駆動するコア ライブラリを介して既に定義されています。
上記の場合、Piral からダッシュボード プラグインのダッシュボード コンテナを定義します。ダッシュボード プラグインは、ダッシュボードを提供します。ダッシュボードは、デフォルトで、/
ページのホームページ ( ) にあります。外観や配置場所など、ここですべてを変更できます (もちろん、ダッシュボードが必要な場合も同様です)。
ダッシュボードは、1 つの画面に多くの情報を収集するため、ポータル アプリケーションに最適です。マイクロフロントエンドの場合、ダッシュボードも優れています — 特にショーケースとして。このページでは、すべてではないにしてもほとんどのマイクロフロントエンドが何かを表示したいと考えています。
ダッシュボード コンテナーから既定のタイルを削除すると、Web アプリはもう少し空っぽに見えるはずです。
Web アプリが空っぽである主な理由は、コンポーネントをどこかにレンダリングするマイクロ フロントエンドを統合していないことです。この時点で、スキャフォールディング メカニズムは、新しいアプリ シェルを Piral 自体が所有する特別なフィード (空のフィード) に接続しました。
空のフィードは、名前が示すように、常に空のままになるフィードです。App Shell のコードを変更して、代わりに他のフィードに移動できます。
このためには、src/index.tsx
ファイルを開く必要があります。そこに、使用するフィードの URL を含む変数が表示されます。
const feedUrl = 'https://feed.piral.cloud/api/v1/pilet/sample';
別のアプリ シェルで使用されているサンプル フィードに移動すると、マイクロ フロントエンドによって適切に入力された場合にシェルがどのように表示されるかを実際に確認できます。ダッシュボードは次のようになります。
新しいアプリ シェルで別のフィードからのピレットを既に表示できるという事実は、実際には非常にクールです。これは、ピレットが本当に独立しており、シェルと強い関係がないことを前もって示しています.
ただし、このようなスムーズな統合が常に可能であるとは限らないことに注意してください。Pilets は、他の Piral インスタンスに簡単に統合できる方法でいつでも開発できます。同様に、さまざまなアプリ シェルを除外する方法でピレットを開発することも可能です。
このアプリ シェル用のマイクロ フロントエンドを開発する前に、そのエミュレーターを作成する必要があります。デバッグ プロセスを停止し、次のコマンドを実行します。
npm run build
これはpiral build
、現在のプロジェクトで実行されます。その結果、 には 2 つのサブディレクトリが存在しdist
ます。
dist/release
dist/emulator
前者は実際に Web アプリをどこかにデプロイするために使用できますが、後者には次の.tgz
ようにレジストリにアップロードできるファイルが含まれています。
npm publish dist/emulator/my-app-shell-1.0.0.tgz
パッケージを公開するには npm 資格情報が必要な場合がありますが、既に npm にログインしている場合でも、公開せずに非公開のままにするか、別のレジストリで公開したくない場合があります。
カスタム レジストリを使用して発行プロセスをテストするには、Verdaccioを使用できます。新しいシェルで、次を開始します。
npx verdaccio
これにより、Verdaccio のローカル バージョンがインストールされ、実行されます。次のようなものが画面に表示されるはずです。
warn --- http address - http://localhost:4873/ - verdaccio/5.13.1
このアドレスにアクセスして、手順を参照してください。それらは次のようになります。
ログイン コマンド ( npm adduser --registry
http://localhost:4873/
) を実行し、データを入力します。Username
との場合Password
は、 をそのまま使用できますtest
。何でも取られます。のEmail
ように簡単にできますfoo@bar.com
。
カスタム レジストリへの公開は、次の方法で機能するようになりました。
npm publish dist/emulator/my-app-shell-1.0.0.tgz --registry http://localhost:4873/
完了したら、このシェルのマイクロフロントエンドを作成できます!
App Shell で行ったように、 を使用しpiral-cli
てプロジェクトを足場にすることができます。このためのコマンドは、pilet
代わりに を使用するようになりpiral-instance
ました。私たちは走る:
npm init pilet --source my-app-shell --registry http://localhost:4873/ --bundler esbuild --target my-pilet --defaults
my-pilet
これにより、新しいマイクロフロントエンドのコードを含む新しいディレクトリが作成されます。ツールは に設定されてesbuild
います (以前と同様に、非常に迅速にインストールされるため esbuild を使用しますが、webpack などの別のものを使用することもできます)。
上記の重要な部分は--source
、開発に使用するエミュレーターを示す を指定することです。すべてが整ったので、次をcd my-pilet
実行できます。
npm start
前述のように、開発サーバーは でホストされていhttp://localhost:1234
ます。そこに移動すると、次のようなページが表示されます。
empty
フィードを使用したかのようにほとんど空です。ただし、この場合、新しいピレットのテンプレートは既にタイルとメニュー項目を登録しています。これを変更する方法を見てみましょう。
ファイルを開き、src/index.tsx
コードを見てください。
import * as React from 'react';
import type { PiletApi } from 'my-app-shell';
export function setup(api: PiletApi) {
api.showNotification('Hello from Piral!', {
autoClose: 2000,
});
api.registerMenu(() =>
<a href="https://docs.piral.io" target="_blank">Documentation</a>
);
api.registerTile(() => <div>Welcome to Piral!</div>, {
initialColumns: 2,
initialRows: 1,
});
}
簡単に言えば、ピレットは単なる JavaScript ライブラリです。重要な部分は、このライブラリがエクスポートするものです。
ピレットはsetup
関数 (正確にはオプションで関数も) をエクスポートしteardown
ます。api
この関数は、マイクロ フロントエンドが接続されると使用され、アプリケーション シェルによって定義および作成される単一の引数 を受け取ります。
アプリ シェルの API (しばしば Pilet API と呼ばれます) は、ピレットがアプリケーション内でパーツを登録できる場所です。ページを追加して、タイルを少し変更してみましょう。
タイルから始めます。teaser
実際に少し背景を持っているようなクラスを与えることができます。さらに、ダッシュボード コンテナーのメタデータを少し追加します。initialColumns
プロパティとプロパティを使用してinitialRows
、目的のサイズを伝えることができます。
app.registerTile(() => <div className="teaser">Hello LogRocket!</div>, {
initialColumns: 2,
initialRows: 2,
})
保存すると、タイルの外観が少し異なります。showNotification
不要になった を削除して、新しいページを導入しましょう。
api.registerPage('/foo', () =>
<p>This is my page</p>
);
このページにリンクするには、登録されているメニュー項目を変更できます。SPA ナビゲーションを実行するには、使い慣れた React ツールを使用できますreact-router-dom
。
api.registerMenu(() =>
<Link to="/foo">Foo</Link>
);
すごい!ただし、ページのようなフラグメントは常に必要なわけではなく、レンダリングする必要がある場合にのみロードする必要があります。この種の遅延読み込みは、コードを専用のファイル (つまり ) に配置しPage.tsx
、登録を次のように変更することで実現できます。
const Page = React.lazy(() => import('./Page'));
api.registerPage('/foo', Page);
の内容は次のPage.tsx
ように簡単です。
import * as React from 'react';
export default () => {
return (
<>
<h1>Page Title</h1>
<p>Lorem ipsum dolor sit ...</p>
<p>Lorem ipsum dolor sit ...</p>
</>
);
};
ページを登録したら、ナビゲーション バーの [Foo] をクリックしてページを表示できます。
ピレットが作成されたので、実際にビルドして公開できます。この時点では、独自のフィードを作成したり、App Shell をどこにも公開したりしていないため、最後の部分は実際には少し理論的なものです。
ピレットを構築するには、次を実行できます。
npm run build
作成したら、 を使用してピレットをパックできますnpx pilet pack
。これは、実行中と非常によく似ていnpm pack
ます。結果は別の.tgz
ファイルです。今回はエミュレータではなく、実際のピレットです。tarball は、フィード サービスなどの専用サービスにアップロードできるもので、アプリ シェルで使用されるフィードを提供します。
このための非商用および商用製品の例は、piral.cloudで見つけることができます。
チュートリアルを終了する前に、1 つの共通機能を統合する方法を見てみましょう。この場合は、SWR を使用して HTTP 要求を実行します。
SWR のような一般的な問題を統合する方法は複数あります。アプリケーション シェルに swr (またはその上にある他のライブラリ) を追加して構成すると、次の 3 つのオプションがあります。
swr
ピレットに委ねる: 分散依存関係として共有できますswr
(つまり、他のピレットがまだロードしていない場合にのみロードします)。SWT を統合するための最も簡単で信頼性の高い方法は、最初のオプションを使用することです。このために、App Shell に戻ります。
App Shell のディレクトリで次を実行します。
npm install swr
では、 を変更してみましょうpackage.json
。ほとんどすべてを保持しますが、セクションのexternals
配列を次のように変更します。pilets
{
"name": "my-app-shell",
"version": "1.1.0",
// ...
"pilets": {
"externals": ["swr"],
// ...
},
// ...
}
バージョン番号も変更したことに注意してください。エミュレーターを更新するため、新しいバージョンが必要です。swr
これは、すべてのピレットと実際に依存関係を共有するように Piral に指示します。
npm run build
これを実際にテストするために、もう一度書いて公開しましょう。
npm run build
npm publish dist/emulator/my-app-shell-1.1.0.tgz --registry http://localhost:4873/
更新されたシェルが利用可能になったら、ピレットのディレクトリに移動してアプリ シェルをアップグレードしましょう。
npx pilet upgrade
ピレットのpackage.json
ファイルが変更されているはずです。の代わりmy-app-shell
にバージョン内への参照が含まれるようになりました。さらに、およびにリストされているはずです。1.1.01.0.0swrdevDependenciespeerDependencies
使用するページを変更しましょうswr
:
import * as React from 'react';
import LogRocket from 'swr';
// note: fetcher could have also been globally configured in the app shell
// however, in general the pilet's don't know about this and so they may want to
// reconfigure or redefine it like here
const fetcher = (resource, init) => fetch(resource, init).then(res => res.json());
export default () => {
const { data, error } = useSWR('https://jsonplaceholder.typicode.com/users/1', fetcher);
if (error) {
return <div>failed to load</div>;
}
if (!data) {
return <div>loading...</div>;
}
return (
<>
Hello {data.name}!
</>
);
};
これで完了です。SWR はアプリで正常にセットアップされただけでなく、すべてのマイクロ フロントエンドでも使用できます。これにより、SWR をロードするための帯域幅と SWR の内部キャッシュの両方が節約され、すべてのマイクロ フロントエンドに優れたパフォーマンス上の利点がもたらされます。
この投稿では、Piral を使い始めるのがいかに簡単かを説明しました。Piral は、異なるチーム間であっても、Web アプリを異なるリポジトリに配布するオプションを提供します。
この投稿では、非常に基本的な設定についてのみ説明しましたが、Piral でできることは他にもたくさんあります。Piral を探索する最善の方法は、公式ドキュメントを参照することです。
Piral が他のほとんどのソリューションよりも拡張性に優れている理由は、Piral が疎結合を促進するためです。このようにすると、2 つのものを融合するのに苦労し、機能の重複や隠れたモノリスを回避するのに役立ちます。
何をするにしても、どの依存関係を共有し、どの依存関係をピレットに任せるかについて、あらかじめ考えておく必要があります。swr
共有依存関係としての提供が実際に数秒で設定された例を見てきました。ハッピーコーディング!
ソース: https://blog.logrocket.com/creating-micro-frontends-piral/
1660015200
A little bit more than three years ago, I revealed the idea of forming micro-frontends via some kind of discovery mechanism in a post for LogRocket, Taming the frontend monolith. The discovery mechanism, known as a feed service, was at the heart of a solution to which I was contributing.
That solution was Piral, which we officially revealed at O’Reilly’s Software Architecture conference half a year later.
Today, Piral is one of the most-used and well-known solutions in the micro-frontend space. That alone would justify another blog post — but then, we also have the increased popularity of micro-frontends, as well as the demand for scalability in general.
See more at: https://blog.logrocket.com/creating-micro-frontends-piral/
#piral #microfrontend
1660025520
Há pouco mais de três anos, revelei a ideia de formar micro-frontends por meio de algum tipo de mecanismo de descoberta em um post para LogRocket, Taming the frontend monolith . O mecanismo de descoberta, conhecido como serviço de alimentação, estava no centro de uma solução para a qual eu estava contribuindo.
Essa solução foi a Piral, que revelamos oficialmente na conferência de arquitetura de software da O'Reilly, meio ano depois.
Hoje, o Piral é uma das soluções mais usadas e conhecidas no espaço micro-frontend. Isso por si só justificaria outra postagem no blog - mas também temos o aumento da popularidade dos micro-frontends, bem como a demanda por escalabilidade em geral.
Sem mais delongas, vamos dar uma olhada no que são micro-frontends, por que o baixo acoplamento é tão importante para eles e como a Piral resolve isso (e outros problemas) para alcançar uma grande escalabilidade. Abordaremos isso nas seguintes seções:
Nos últimos anos, os micro-frontends tornaram-se cada vez mais populares. Uma razão para isso é o aumento da demanda por aplicativos da Web maiores. Hoje, portais poderosos, como os portais AWS e Azure, juntamente com ricas experiências de usuário como Netflix ou DAZN, não são a exceção, mas a norma. Como podem ser construídos aplicativos tão grandes? Como eles podem ser dimensionados?
Uma resposta para essas perguntas pode ser o uso de microfrontends. Um microfrontend é a representação técnica de um subdomínio de negócios . A ideia básica é desenvolver uma parte da interface do usuário isoladamente. Esta peça não precisa ser representada na tela em uma área; na verdade, ele pode consistir em vários fragmentos, ou seja, um item de menu e a página à qual o item de menu está vinculado. A única restrição é que a peça deve estar relacionada a um subdomínio de negócios.
Um microfrontend consiste em diferentes componentes, mas esses não são os componentes clássicos da interface do usuário, como menus suspensos ou caixas de campo de rich text. Em vez disso, esses componentes são componentes específicos do domínio e contêm alguma lógica de negócios, como quais solicitações de API precisam ser feitas.
Mesmo uma coisa simples como um item de menu é um componente de domínio neste contexto porque já saberá que o link para a página é do mesmo domínio comercial. Uma vez que o componente tenha alguma lógica de domínio, ele é um componente de domínio — e pode, portanto, fazer parte de um micro-frontend.
Para implementar micro-frontends, existe todo um conjunto de abordagens e práticas. Eles podem ser reunidos em tempo de construção, no lado do servidor e no lado do cliente.
Neste artigo, veremos a composição no lado do cliente, mas a mesma história pode ser escrita para o servidor. Então, como os micro-frontends realmente escalam?
Muitas estruturas de microfrontend enfrentam problemas de escalabilidade em contextos do mundo real. Olhando para outros artigos, a tecnologia parece boa à primeira vista; por exemplo, se você ler Criando aplicativos de microfrontends com spa único ou Construindo microfrontends Svelte com Podium , eles apresentam a tecnologia e o caso de uso bem. Outro exemplo pode ser visto em Construir micro-frontends progressivos com Fronts .
O problema é que esses frameworks geralmente tentam dividir a interface do usuário visualmente. No entanto, na realidade, você nunca dividirá seu frontend em partes como “navegação”, “cabeçalho”, “conteúdo” e “rodapé”. Por que é que?
Um aplicativo real é composto de diferentes partes que vêm, conforme explicado na seção anterior, de diferentes subdomínios que se unem para formar o domínio completo do aplicativo. Embora esses subdomínios possam ser totalmente separados no papel, eles geralmente aparecem para o usuário final dentro dos mesmos elementos de layout.
Pense em algo como uma loja virtual. Se você tiver um subdomínio para os detalhes do produto e outro subdomínio lidando com pedidos anteriores, não gostaria de ver apenas IDs sem sentido de produtos em seu histórico de pedidos como usuário. Em vez disso, você esperaria que pelo menos o nome do produto e alguns detalhes fossem mostrados no histórico de pedidos. Assim, esses subdomínios se intercalam visualmente para o usuário final.
Da mesma forma, praticamente todos os subdomínios têm algo a contribuir para elementos de layout de UI compartilhados, como navegação, cabeçalho ou rodapé. Portanto, ter microfrontends que tratem exclusivamente de uma área de navegação não faz muito sentido na prática, pois esse microfrontend receberá muitas solicitações de outras equipes — e se tornará um gargalo. Fazer isso resultará em um monólito oculto.
Agora, alguém pode argumentar que não ter a navegação em um micro-frontend resultaria na mesma demanda de mudanças, mas desta vez no proprietário do shell do aplicativo. Isso seria ainda pior.
Então qual é a solução então? Claramente, precisamos dissociar essas coisas. Então, em vez de usar algo como:
import MyMenuItem1 from 'my-micro-frontend1';
import MyMenuItem1 from 'my-micro-frontend2';
import MyMenuItemN from 'my-micro-frontendN';
const MyMenu = () => (
<>
<MyMenuItem1 />
<MyMenuItem2 />
<MyMenuItemN />
</>
);
Precisamos registrar cada uma das partes necessárias, como os itens de navegação dos próprios micro-frontends. Dessa forma, poderíamos acabar com uma estrutura como:
const MyMenu = () => {
const items = useRegisteredMenuItems();
return (
<>
{items.map(({ id, Component }) => <Component key={id} />)}
</>
);
};
Para evitar a necessidade de conhecer os nomes e locais de um micro-frontend, é necessário um tipo de descoberta. Este é apenas um documento JSON que pode ser recuperado de um local conhecido, como um serviço de back-end.
Agora que sabemos o que precisamos dimensionar, é hora de começar a implementar. Felizmente, existe um framework que já nos dá uma vantagem nisso: o Piral.
Piral é uma estrutura para criar aplicativos da Web ultraescaláveis usando microfrontends. Entre muitas coisas, possui:
Dessa forma, equipes individuais podem se concentrar em seus problemas de domínio específicos, em vez de exigir alinhamento e lançamentos conjuntos. Um aplicativo Piral consiste em três partes:
Toda a configuração pode ser esboçada da seguinte forma:
Os desenvolvedores de módulos podem usar o utilitário de linha de comando piral-cli
para criar scaffold (ou seja, criar com algum modelo) e publicar novos pilets, depurar e atualizar módulos existentes ou realizar alguns linting e validações. Os usuários reais não veem a solução como peças diferentes – na verdade, eles consomem o shell do aplicativo junto com os pilets em um aplicativo. Tecnicamente, esses pilets são obtidos de um serviço de alimentação.
Muitas vezes, a experiência de desenvolvimento para micro-frontends não é ideal. Ou várias coisas precisam ser verificadas e iniciadas, ou todo o processo se resume a um ciclo desenvolver-enviar-tentar-falhar-reiniciar. O Piral é diferente aqui - ele tenta ser o primeiro offline. Micro-frontends são desenvolvidos diretamente em uma edição especial do shell do aplicativo conhecido como emulador.
O emulador é apenas um pacote npm que pode ser instalado em qualquer projeto npm. Quando o piral-cli
é usado para depuração, ele usará o conteúdo do emulador como a página a ser exibida. O pilet será então servido através de uma API interna em vez de ir para um serviço de alimentação remoto ou algo semelhante.
No entanto, ainda pode fazer sentido carregar micro-frontends existentes durante o desenvolvimento. Nesses casos, as pilhas de uma ração existente ainda podem ser integradas.
Vamos ver como tudo isso funciona na prática.
Existem várias maneiras de criar um shell de aplicativo com o Piral:
piral-cli
para criar um novo projetoNeste post, faremos o último.
Na linha de comando, executamos:
npm init piral-instance --bundler esbuild --target my-app-shell --defaults
Isso criará um novo shell de aplicativo no my-app-shell
diretório. O projeto usará npm, TypeScript e as esbuild
ferramentas como nosso empacotador (embora pudéssemos ter selecionado qualquer tipo de empacotador, como webpack, Parcel ou Vite, etc.). A escolha esbuild
deve ser suficiente em muitos casos e oferece o benefício do tempo de instalação mais rápido.
Agora, podemos começar a depurar o projeto. Vá para o novo diretório (por exemplo, cd my-app-shell
) e inicie a sessão de depuração:
npm start
Indo para http://localhost:1234
deve mostrar o modelo padrão:
Agora podemos alterar o modelo em todos os aspectos possíveis. Por exemplo, poderíamos alterar o layout fornecido para não ter nenhum bloco de conteúdo fixo; basta editar o src/layout.tsx
arquivo e remover o defaultTiles
e o defaultMenuItems
. Certifique-se não apenas de remover sua inicialização, mas também as referências a eles.
Para obter mais detalhes, podemos alterar o DashboardContainer
de:
DashboardContainer: ({ children }) => (
<div>
<h1>Hello, world!</h1>
<p>Welcome to your new microfrontend app shell, built with:</p>
<div className="tiles">
{defaultTiles}
{children}
</div>
</div>
),
para:
DashboardContainer: ({ children }) => (
<div>
<h1>Hello, world!</h1>
<p>Welcome to your new microfrontend app shell, built with:</p>
<div className="tiles">
{children}
</div>
</div>
),
Todos os componentes que podem ser vistos aqui servem a propósitos diferentes. Enquanto muitos deles vêm de plugins opcionais, alguns – como o ErrorInfo
ou Layout
– já estão definidos através da biblioteca principal que impulsiona o Piral.
No caso acima, definimos o contêiner do painel para o plug-in do painel da Piral. O plugin do painel nos dá um painel, que — por padrão — está localizado na página inicial ( /
) de nossa página. Podemos alterar tudo aqui, incluindo a aparência e onde deve estar localizado (e, é claro, se queremos ter um painel).
Os painéis são ótimos para aplicativos de portal porque reúnem muitas informações em uma tela. Para micro-frontends, um painel também é bom – especialmente como vitrine. Nesta página, potencialmente a maioria, se não todos, os micro-frontends querem mostrar algo.
Depois de remover os blocos padrão do contêiner do painel, o aplicativo da Web agora deve parecer um pouco mais vazio:
A principal razão para o vazio de nosso aplicativo da web é que não integramos nenhum micro-frontend que renderiza componentes em algum lugar. Nesse ponto, o mecanismo de andaimes conectou nosso novo shell de aplicativo a um feed especial que pertence à própria Piral: o feed vazio.
O feed vazio é um feed que, como o nome sugere, sempre permanecerá vazio. Podemos alterar o código do shell do aplicativo para outro feed.
Para isso, você precisa abrir o src/index.tsx
arquivo. Lá, você verá a variável com o URL do feed a ser usado:
const feedUrl = 'https://feed.piral.cloud/api/v1/pilet/sample';
Indo para o feed de amostra, que é usado em outro shell de aplicativo , podemos ver como o shell ficaria se preenchido corretamente por micro-frontends. O painel agora deve se parecer com:
O fato de já podermos exibir pilets de outro feed em nosso novo shell de aplicativo é realmente muito legal. Isso indica de antemão que os pilets são realmente independentes e não têm forte relação com sua concha.
No entanto, tenha em mente que uma integração tão suave nem sempre é possível. Pilets sempre podem ser desenvolvidos de uma forma que seria bastante fácil de integrar em outras instâncias do Piral. Da mesma forma, também é possível desenvolver um pilet de uma maneira que exclua diferentes shells de aplicativos.
Antes de podermos desenvolver alguns micro-frontends para este shell de aplicativo, precisamos criar seu emulador. Pare o processo de depuração e execute o seguinte comando:
npm run build
Isso será executado piral build
no projeto atual. O resultado é que existem dois subdiretórios em dist
:
dist/release
dist/emulator
Enquanto o primeiro pode ser usado para realmente implantar nosso aplicativo da web em algum lugar, o último contém um .tgz
arquivo que pode ser carregado em um registro como este:
npm publish dist/emulator/my-app-shell-1.0.0.tgz
Você pode precisar de credenciais do npm para publicar o pacote, mas mesmo se você já estiver conectado ao npm, talvez não queira publicá-lo e, em vez disso, mantê-lo privado ou publicar em um registro diferente.
Para testar o processo de publicação com um registro personalizado, você pode usar o Verdaccio . Em um novo shell, inicie:
npx verdaccio
Isso instalará e executará uma versão local do Verdaccio. Você deve ver algo como o seguinte impresso na tela:
warn --- http address - http://localhost:4873/ - verdaccio/5.13.1
Acesse este endereço e veja as instruções. Eles devem se parecer com:
Execute o comando login ( npm adduser --registry
http://localhost:4873/
) e preencha os dados. Para Username
e Password
, você pode simplesmente usar test
. Qualquer coisa será tomada; o Email
pode ser tão simples quanto foo@bar.com
.
A publicação no registro personalizado agora funciona por meio de:
npm publish dist/emulator/my-app-shell-1.0.0.tgz --registry http://localhost:4873/
Uma vez feito, podemos criar micro-frontends para este shell!
Assim como fizemos para o shell do aplicativo, podemos usar o piral-cli
para nos estruturar um projeto. O comando para isso agora usa pilet
em vez de piral-instance
. Nós corremos:
npm init pilet --source my-app-shell --registry http://localhost:4873/ --bundler esbuild --target my-pilet --defaults
Isso criará um novo diretório chamado my-pilet
que contém o código para um novo micro-frontend. O conjunto de ferramentas está definido para esbuild
(como antes, usamos esbuild porque ele é instalado muito rapidamente, mas você também pode optar por algo diferente, como webpack ).
A parte importante acima é especificar o --source
, que indica o emulador a ser usado para desenvolvimento. Agora que tudo está no lugar, podemos cd my-pilet
e executar:
npm start
Como antes, o servidor de desenvolvimento está hospedado em http://localhost:1234
. Ir para lá resulta em uma página como mostrado abaixo:
Quase tão vazio como se tivéssemos usado o empty
feed. Porém, neste caso, o template para a nova pilha já registra um ladrilho e um item de menu. Vamos ver como podemos mudar isso.
Abra o src/index.tsx
arquivo e dê uma olhada no código:
import * as React from 'react';
import type { PiletApi } from 'my-app-shell';
export function setup(api: PiletApi) {
api.showNotification('Hello from Piral!', {
autoClose: 2000,
});
api.registerMenu(() =>
<a href="https://docs.piral.io" target="_blank">Documentation</a>
);
api.registerTile(() => <div>Welcome to Piral!</div>, {
initialColumns: 2,
initialRows: 1,
});
}
Simplesmente falando, um pilet é apenas uma biblioteca JavaScript; a parte importante é o que esta biblioteca exporta.
Um pilet exporta uma setup
função (e, opcionalmente, uma teardown
função, para ser mais preciso). Esta função é usada quando o micro-frontend está conectado e recebe um único argumento, api
, que é definido e criado pelo shell do aplicativo.
A API do shell do aplicativo (geralmente chamada de API Pilet) é o local onde os pilets podem registrar suas partes no aplicativo. Vamos adicionar uma página e mudar um pouco o bloco.
Começamos com o azulejo. Podemos dar-lhe alguma classe como teaser
ter um pouco de fundo. Além disso, queremos adicionar um pouco de metadados para o contêiner do painel. Podemos usar as propriedades initialColumns
e initialRows
para comunicar o tamanho desejado.
app.registerTile(() => <div className="teaser">Hello LogRocket!</div>, {
initialColumns: 2,
initialRows: 2,
})
Uma vez salvo, o bloco terá uma aparência um pouco diferente. Vamos remover o showNotification
que não é mais necessário e introduzir uma nova página:
api.registerPage('/foo', () =>
<p>This is my page</p>
);
Para vincular a esta página, podemos alterar o item de menu registrado. Para realizar a navegação do SPA, podemos usar uma ferramenta React familiar, react-router-dom
:
api.registerMenu(() =>
<Link to="/foo">Foo</Link>
);
Excelente! No entanto, fragmentos como a página nem sempre são necessários e só devem ser carregados quando devem ser renderizados. Esse tipo de carregamento lento pode ser obtido colocando o código em um arquivo dedicado, ou seja, Page.tsx
, e alterando o registro para:
const Page = React.lazy(() => import('./Page'));
api.registerPage('/foo', Page);
O conteúdo de Page.tsx
pode ser tão simples quanto:
import * as React from 'react';
export default () => {
return (
<>
<h1>Page Title</h1>
<p>Lorem ipsum dolor sit ...</p>
<p>Lorem ipsum dolor sit ...</p>
</>
);
};
Com a página cadastrada, você já pode clicar em “Foo” na barra de navegação e ver a página:
Agora que nosso pilet está escrito, podemos realmente construí-lo e publicá-lo. Neste ponto, não criamos nosso próprio feed ou publicamos o shell do aplicativo em nenhum lugar, então a última parte é realmente um pouco teórica.
Para construir a pilha, você pode executar:
npm run build
Uma vez criado, você pode embalar a pilha usando npx pilet pack
. Isso será bastante semelhante à execução npm pack
. O resultado é outro .tgz
arquivo - desta vez não um emulador, mas o próprio pilet. O tarball é o que pode ser carregado em um serviço dedicado, como um serviço de feed, oferecendo o feed para ser consumido pelo shell do aplicativo.
Exemplos de ofertas não comerciais e comerciais para isso podem ser encontrados em piral.cloud .
Antes de encerrarmos o tutorial, vamos ver como podemos integrar uma funcionalidade comum — neste caso, realizar solicitações HTTP com SWR.
Existem várias maneiras de integrar preocupações comuns como SWR. Depois de adicionar swr (ou outras bibliotecas sobre ele) ao shell do aplicativo e configurá-lo lá, você tem três opções:
swr
para os pilets: eles podem compartilhar swr
como uma dependência distribuída (ou seja, apenas carregá-lo quando nenhum outro pilets o carregou ainda)O método mais fácil e confiável para integrar o SWT seria usar a primeira opção. Para isso, voltamos ao shell do aplicativo.
No diretório do shell do aplicativo, execute:
npm install swr
Agora vamos modificar o package.json
. Mantenha praticamente tudo, mas modifique o externals
array da pilets
seção assim:
{
"name": "my-app-shell",
"version": "1.1.0",
// ...
"pilets": {
"externals": ["swr"],
// ...
},
// ...
}
Observe que também alterei o número da versão. Como faremos uma atualização em nosso emulador, precisamos ter uma nova versão. Isso instruirá o Piral a realmente compartilhar a swr
dependência com todos os pilets.
Para realmente testar isso, vamos escrever npm run build
e publicar novamente.
npm run build
npm publish dist/emulator/my-app-shell-1.1.0.tgz --registry http://localhost:4873/
Com o shell atualizado disponível, vamos entrar no diretório do pilet e atualizar o shell do aplicativo:
npx pilet upgrade
O package.json
arquivo para o pilet deveria ter mudado. Ele agora deve conter uma referência a my-app-shell
in version em 1.1.0
vez de 1.0.0
. Além disso, você deve ver swr
listado em devDependencies
e peerDependencies
.
Vamos modificar a página para usar swr
:
import * as React from 'react';
import LogRocket from 'swr';
// note: fetcher could have also been globally configured in the app shell
// however, in general the pilet's don't know about this and so they may want to
// reconfigure or redefine it like here
const fetcher = (resource, init) => fetch(resource, init).then(res => res.json());
export default () => {
const { data, error } = useSWR('https://jsonplaceholder.typicode.com/users/1', fetcher);
if (error) {
return <div>failed to load</div>;
}
if (!data) {
return <div>loading...</div>;
}
return (
<>
Hello {data.name}!
</>
);
};
E agora terminamos! O SWR não é apenas configurado com sucesso em nosso aplicativo, mas também podemos usá-lo em todos os micro-frontends. Isso economiza a largura de banda para carregar o SWR e o cache interno do SWR, dando a todos os micro-frontends um bom benefício de desempenho.
Neste post, você viu como é fácil começar a usar o Piral. A Piral oferece a opção de distribuir seu aplicativo da Web em diferentes repositórios, mesmo em diferentes equipes.
Exploramos apenas a configuração básica neste post, mas há muito mais que você pode fazer com o Piral. A melhor maneira de explorar o Piral é consultar a documentação oficial .
A razão pela qual o Piral escala melhor do que a maioria das outras soluções é que o Piral incentiva o acoplamento fraco. Dessa forma, você terá dificuldade em fundir duas coisas, o que ajuda a evitar sobreposição de recursos e monólitos ocultos.
O que quer que você planeje fazer, certifique-se de já pensar em quais dependências compartilhar e quais deixar para o pilet. Vimos um exemplo em que o fornecimento swr
como uma dependência compartilhada foi configurado em segundos. Boa codificação!
Fonte: https://blog.logrocket.com/creating-micro-frontends-piral/
1660021572
Hace un poco más de tres años, revelé la idea de formar micro-frontends a través de algún tipo de mecanismo de descubrimiento en una publicación para LogRocket, Domando el monolito de frontend . El mecanismo de descubrimiento, conocido como servicio de alimentación, estaba en el centro de una solución a la que yo estaba contribuyendo.
Esa solución fue Piral, que revelamos oficialmente en la conferencia de arquitectura de software de O'Reilly medio año después.
Hoy en día, Piral es una de las soluciones más utilizadas y conocidas en el espacio micro-frontend. Eso por sí solo justificaría otra publicación de blog, pero también tenemos la creciente popularidad de las micro-frontends, así como la demanda de escalabilidad en general.
Sin más preámbulos, echemos un vistazo a qué son los micro-frontends, por qué el acoplamiento flexible es tan importante para ellos y cómo Piral resuelve este (y otros problemas) para lograr una gran escalabilidad. Cubriremos esto en las siguientes secciones:
En los últimos años, las micro-frontends se han vuelto cada vez más populares. Una de las razones de esto es la mayor demanda de aplicaciones web más grandes. Hoy en día, los portales poderosos como AWS y Azure Portals, junto con las ricas experiencias de usuario como Netflix o DAZN, no son la excepción, sino la norma. ¿Cómo se pueden construir aplicaciones tan grandes? ¿Cómo se pueden escalar?
Una respuesta a esas preguntas puede ser usar micro-frontends. Una micro-frontend es la representación técnica de un subdominio comercial . La idea básica es desarrollar una parte de la interfaz de usuario de forma aislada. Esta pieza no necesita estar representada en la pantalla en un área; en realidad puede constar de varios fragmentos, es decir, un elemento de menú y la página a la que se vincula el elemento de menú. La única restricción es que la pieza debe estar relacionada con un subdominio comercial.
Una micro-frontend consta de diferentes componentes, pero estos no son los componentes clásicos de la interfaz de usuario, como los menús desplegables o los cuadros de campo de texto enriquecido. En cambio, estos componentes son componentes específicos del dominio y contienen cierta lógica comercial, como qué solicitudes de API deben realizarse.
Incluso algo simple como un elemento de menú es un componente de dominio en este contexto porque ya sabrá que el enlace a la página es del mismo dominio empresarial. Una vez que el componente tiene alguna lógica de dominio, es un componente de dominio y, por lo tanto, puede ser parte de una micro-interfaz.
Para implementar micro-frontends, existe todo un conjunto de enfoques y prácticas. Se pueden reunir en el momento de la compilación, en el lado del servidor y en el lado del cliente.
En este artículo veremos la composición en el lado del cliente, pero se podría escribir la misma historia para el servidor. Entonces, ¿cómo se escalan realmente las micro-frontends?
Muchos marcos de micro-frontend enfrentan problemas de escalabilidad en contextos del mundo real. Mirando otros artículos, la tecnología parece sólida al principio; por ejemplo, si lee Creando aplicaciones de micro-frontends con un solo spa o Building Svelte micro-frontends con Podium , presentan bien la tecnología y el caso de uso. Otro ejemplo se puede ver en Construir micro-frontends progresivos con Fronts .
El problema es que estos marcos generalmente intentan dividir la interfaz de usuario visualmente. Sin embargo, en realidad, nunca dividirá su interfaz en partes como "navegación", "encabezado", "contenido" y "pie de página". ¿Porqué es eso?
Una aplicación real se compone de diferentes partes que provienen, como se explicó en la sección anterior, de diferentes subdominios que se unen para formar el dominio completo de la aplicación. Si bien estos subdominios se pueden separar completamente en papel, generalmente aparecen para el usuario final dentro de los mismos elementos de diseño.
Piense en algo como una tienda web. Si tiene un subdominio para los detalles del producto y otro subdominio que maneja pedidos anteriores, entonces no querrá ver solo ID de productos sin sentido en su historial de pedidos como usuario. En cambio, esperaría que al menos el nombre del producto y algunos detalles se muestren en el historial de pedidos. Entonces, estos subdominios se intercalan visualmente hacia el usuario final.
Del mismo modo, prácticamente casi todos los subdominios tienen algo que aportar a los elementos de diseño de la interfaz de usuario compartidos, como una navegación, un encabezado o un pie de página. Por lo tanto, tener micro-frontends que se ocupen exclusivamente de un área de navegación no tiene mucho sentido en la práctica porque este micro-frontend recibirá muchas solicitudes de otros equipos y se convertirá en un cuello de botella. Hacer eso dará como resultado un monolito oculto.
Ahora, alguien puede argumentar que no tener la navegación en una micro-interfaz resultaría en la misma demanda de cambios, pero esta vez en el propietario de la aplicación. Esto sería aún peor.
Entonces, ¿cuál es la solución entonces? Claramente, necesitamos desacoplar estas cosas. Entonces, en lugar de usar algo como:
import MyMenuItem1 from 'my-micro-frontend1';
import MyMenuItem1 from 'my-micro-frontend2';
import MyMenuItemN from 'my-micro-frontendN';
const MyMenu = () => (
<>
<MyMenuItem1 />
<MyMenuItem2 />
<MyMenuItemN />
</>
);
Necesitamos registrar cada una de las partes necesarias, como los elementos de navegación de los propios micro-frontends. De esta forma, podríamos terminar con una estructura como:
const MyMenu = () => {
const items = useRegisteredMenuItems();
return (
<>
{items.map(({ id, Component }) => <Component key={id} />)}
</>
);
};
Para evitar la necesidad de conocer los nombres y las ubicaciones de una micro interfaz, se necesita una especie de descubrimiento. Este es solo un documento JSON que se puede recuperar desde una ubicación conocida, como un servicio de back-end.
Ahora que sabemos lo que necesitamos para escalar, es hora de comenzar a implementar. Afortunadamente, hay un marco que ya nos da una ventaja en esto: Piral.
Piral es un marco para crear aplicaciones web ultraescalables utilizando micro-frontends. Entre muchas cosas, cuenta con:
De esta forma, los equipos individuales pueden concentrarse en sus problemas de dominio específicos, en lugar de requerir alineación y lanzamientos conjuntos. Una aplicación Piral consta de tres partes:
Toda la configuración se puede esbozar de la siguiente manera:
Los desarrolladores de módulos pueden usar la utilidad de línea de comandos piral-cli
para andamiar (es decir, crear con alguna plantilla) y publicar nuevos pilotes, depurar y actualizar módulos existentes, o realizar algunas validaciones y desforrado. Los usuarios reales no ven la solución como piezas diferentes; en realidad, consumen el shell de la aplicación junto con los pilotes en una sola aplicación. Técnicamente, estos pilotes se obtienen de un servicio de alimentación.
Muy a menudo, la experiencia de desarrollo para micro-frontends no es ideal. Es necesario verificar e iniciar varias cosas, o todo el proceso se reduce a un ciclo de desarrollo-envío-intento-fallo-reinicio. Piral es diferente aquí: trata de estar fuera de línea primero. Las micro-frontends se desarrollan directamente en una edición especial del shell de la aplicación conocida como emulador.
El emulador es solo un paquete npm que se puede instalar en cualquier proyecto npm. Cuando piral-cli
se usa para la depuración, en realidad usará el contenido del emulador como la página para mostrar. Luego, el pilet se servirá a través de una API interna en lugar de ir a un servicio de alimentación remota o algo similar.
Sin embargo, aún puede tener sentido cargar micro-frontends existentes durante el desarrollo. En tales casos, aún se pueden integrar pilotes de una alimentación existente.
Veamos cómo funciona todo esto en la práctica.
Hay varias formas de crear un shell de aplicación con Piral:
piral-cli
para crear un nuevo proyectoEn esta publicación, haremos esto último.
En la línea de comando, ejecutamos:
npm init piral-instance --bundler esbuild --target my-app-shell --defaults
Esto creará un nuevo shell de aplicación en el my-app-shell
directorio. El proyecto utilizará npm, TypeScript y las esbuild
herramientas como nuestro paquete (aunque en realidad podríamos haber seleccionado cualquier tipo de paquete, como webpack, Parcel o Vite, etc.). Elegir esbuild
debería ser suficiente en muchos casos y proporciona el beneficio del tiempo de instalación más rápido.
Ahora, podemos comenzar a depurar el proyecto. Vaya al nuevo directorio (p. ej., cd my-app-shell
) e inicie la sesión de depuración:
npm start
Ir a http://localhost:1234
debería mostrarle la plantilla estándar:
Ya podemos cambiar la plantilla en todos los aspectos posibles. Por ejemplo, podríamos cambiar el diseño proporcionado para que no tenga ningún mosaico de contenido fijo; simplemente edite el src/layout.tsx
archivo y elimine el defaultTiles
y el defaultMenuItems
. Asegúrese no solo de eliminar su inicialización, sino también las referencias a ellos.
Para obtener más detalles, podemos cambiar el DashboardContainer
de:
DashboardContainer: ({ children }) => (
<div>
<h1>Hello, world!</h1>
<p>Welcome to your new microfrontend app shell, built with:</p>
<div className="tiles">
{defaultTiles}
{children}
</div>
</div>
),
a:
DashboardContainer: ({ children }) => (
<div>
<h1>Hello, world!</h1>
<p>Welcome to your new microfrontend app shell, built with:</p>
<div className="tiles">
{children}
</div>
</div>
),
Todos los componentes que se pueden ver aquí sirven para diferentes propósitos. Si bien muchos de ellos provienen de complementos opcionales, algunos, como el ErrorInfo
o Layout
, ya están definidos a través de la biblioteca central que impulsa a Piral.
En el caso anterior, definimos el contenedor del tablero para el complemento del tablero de Piral. El complemento del tablero nos brinda un tablero que, de forma predeterminada, se encuentra en la página de inicio ( /
) de nuestra página. Podemos cambiar todo aquí, incluido su aspecto y dónde debería estar ubicado (y, por supuesto, si queremos tener un tablero).
Los tableros son excelentes para las aplicaciones de portal porque recopilan mucha información en una pantalla. Para micro-frontends, un tablero también es bueno, especialmente como escaparate. En esta página, potencialmente la mayoría, si no todas, las micro-frontends quieren mostrar algo.
Después de eliminar los mosaicos predeterminados del contenedor del tablero, la aplicación web ahora debería verse un poco más vacía:
La razón principal por la que nuestra aplicación web está vacía es que no integramos micro-frontends que rendericen componentes en alguna parte. En este punto, el mecanismo de andamiaje conectó nuestro nuevo shell de aplicación a un feed especial que es propiedad de Piral: el feed vacío.
El feed vacío es un feed que, como su nombre indica, permanecerá siempre vacío. Podemos cambiar el código de la shell de la aplicación para que vaya a otro feed en su lugar.
Para ello, debe abrir el src/index.tsx
archivo. Allí verás la variable con la URL del feed a utilizar:
const feedUrl = 'https://feed.piral.cloud/api/v1/pilet/sample';
Al ir al feed de muestra, que se usa en otro shell de la aplicación , podemos ver cómo se vería el shell si estuviera correctamente poblado por micro-frontends. El tablero ahora debería verse así:
El hecho de que ya podamos mostrar pilas de otro feed en nuestra nueva aplicación es realmente genial. Esto indica de antemano que los pilotes son realmente independientes y no tienen una fuerte relación con su caparazón.
Sin embargo, tenga en cuenta que una integración tan fluida no siempre es posible. Pilets siempre se pueden desarrollar de una manera que sería bastante fácil de integrar en otras instancias de Piral. Del mismo modo, también es posible desarrollar un pilet de manera que descarte diferentes shells de aplicaciones.
Antes de que podamos desarrollar algunas micro-frontends para el shell de esta aplicación, debemos crear su emulador. Detenga el proceso de depuración y ejecute el siguiente comando:
npm run build
Esto se ejecutará piral build
en el proyecto actual. El resultado es que hay dos subdirectorios en dist
:
dist/release
dist/emulator
Si bien el primero se puede usar para implementar nuestra aplicación web en algún lugar, el segundo contiene un .tgz
archivo que se puede cargar en un registro como este:
npm publish dist/emulator/my-app-shell-1.0.0.tgz
Es posible que necesite las credenciales de npm para publicar el paquete, pero incluso si ya inició sesión en npm, es posible que no desee publicarlo y, en su lugar, mantenerlo privado o publicarlo en un registro diferente.
Para probar el proceso de publicación con un registro personalizado, puede utilizar Verdaccio . En un nuevo shell, comience:
npx verdaccio
Esto instalará y ejecutará una versión local de Verdaccio. Debería ver algo como lo siguiente impreso en la pantalla:
warn --- http address - http://localhost:4873/ - verdaccio/5.13.1
Vaya a esta dirección y vea las instrucciones. Deberían verse como:
Ejecute el comando de inicio de sesión ( npm adduser --registry
http://localhost:4873/
) y complete los datos. Para Username
y Password
, solo puedes usar test
. Cualquier cosa será tomada; el Email
puede ser tan simple como foo@bar.com
.
La publicación en el registro personalizado ahora funciona a través de:
npm publish dist/emulator/my-app-shell-1.0.0.tgz --registry http://localhost:4873/
Una vez hecho esto, ¡podemos crear micro-frontends para este shell!
Al igual que hicimos con el shell de la aplicación, podemos usar el piral-cli
andamiaje para un proyecto. El comando para esto ahora usa pilet
en lugar de piral-instance
. Corremos:
npm init pilet --source my-app-shell --registry http://localhost:4873/ --bundler esbuild --target my-pilet --defaults
Esto creará un nuevo directorio llamado my-pilet
que contiene el código para una nueva micro-interfaz. Las herramientas están configuradas para esbuild
(como antes, usamos esbuild porque se instala muy rápido, pero también podría optar por algo diferente como webpack ).
La parte importante anterior es especificar el --source
, que indica el emulador que se usará para el desarrollo. Ahora que todo está en su lugar, podemos cd my-pilet
y ejecutamos:
npm start
Como antes, el servidor de desarrollo está alojado en http://localhost:1234
. Ir allí da como resultado una página como la que se muestra a continuación:
Casi tan vacío como si hubiéramos usado el empty
alimentador. Sin embargo, en este caso, la plantilla para el nuevo pilote ya registra un mosaico y un elemento de menú. Veamos cómo podemos cambiar esto.
Abra el src/index.tsx
archivo y eche un vistazo al código:
import * as React from 'react';
import type { PiletApi } from 'my-app-shell';
export function setup(api: PiletApi) {
api.showNotification('Hello from Piral!', {
autoClose: 2000,
});
api.registerMenu(() =>
<a href="https://docs.piral.io" target="_blank">Documentation</a>
);
api.registerTile(() => <div>Welcome to Piral!</div>, {
initialColumns: 2,
initialRows: 1,
});
}
Simplemente hablando, un pilet es solo una biblioteca de JavaScript; lo importante es lo que exporta esta biblioteca.
Un pilote exporta una setup
función (y, opcionalmente, una teardown
función, para ser precisos). Esta función se usa una vez que la micro-frontend está conectada y recibe un solo argumento, api
que es definido y creado por el shell de la aplicación.
La API de la carcasa de la aplicación (a menudo denominada Pilet API) es el lugar donde los pilotes pueden registrar sus piezas dentro de la aplicación. Agreguemos una página y cambiemos un poco el mosaico.
Empezamos con el azulejo. Podemos darle un poco de clase como teaser
para tener un poco de fondo. Además, queremos agregar un poco de metadatos para el contenedor del tablero. Podemos usar las propiedades initialColumns
y initialRows
para comunicar el tamaño deseado.
app.registerTile(() => <div className="teaser">Hello LogRocket!</div>, {
initialColumns: 2,
initialRows: 2,
})
Una vez guardado, el mosaico tendrá un aspecto un poco diferente. Eliminemos lo showNotification
que ya no se necesita e introduzcamos una nueva página:
api.registerPage('/foo', () =>
<p>This is my page</p>
);
Para vincular a esta página, podemos cambiar el elemento de menú registrado. Para realizar la navegación SPA, podemos usar una herramienta React familiar react-router-dom
:
api.registerMenu(() =>
<Link to="/foo">Foo</Link>
);
¡Excelente! Sin embargo, los fragmentos como la página no siempre son necesarios y solo deben cargarse cuando deben renderizarse. Este tipo de carga diferida se puede lograr colocando el código en un archivo dedicado, es decir, Page.tsx
y cambiando el registro para que sea:
const Page = React.lazy(() => import('./Page'));
api.registerPage('/foo', Page);
El contenido de Page.tsx
puede ser tan sencillo como:
import * as React from 'react';
export default () => {
return (
<>
<h1>Page Title</h1>
<p>Lorem ipsum dolor sit ...</p>
<p>Lorem ipsum dolor sit ...</p>
</>
);
};
Con la página registrada, ahora puede hacer clic en "Foo" en la barra de navegación y ver la página:
Ahora que nuestro pilet está escrito, podemos construirlo y publicarlo. En este punto, no hemos creado nuestro propio feed ni publicado el shell de la aplicación en ninguna parte, por lo que la última parte es en realidad un poco teórica.
Para construir el pilet, puedes ejecutar:
npm run build
Una vez creado, puede empacar el pilote usando npx pilet pack
. Esto será bastante similar a ejecutar npm pack
. El resultado es otro .tgz
archivo, esta vez no un emulador, sino el pilet real. El tarball es lo que se puede cargar en un servicio dedicado, como un servicio de feed, que ofrece el feed para que lo consuma el shell de la aplicación.
Se pueden encontrar ejemplos de ofertas no comerciales y comerciales para esto en piral.cloud .
Antes de cerrar el tutorial, veamos cómo podemos integrar una funcionalidad común, en este caso, realizar solicitudes HTTP con SWR.
Hay múltiples formas de integrar preocupaciones comunes como SWR. Una vez que agrega swr (u otras bibliotecas que se encuentran encima) al shell de la aplicación y lo configura allí, tiene tres opciones:
swr
pilotes: pueden compartir swr
como una dependencia distribuida (es decir, solo cargarlo cuando ningún otro pilote lo haya cargado todavía)El método más fácil y confiable para integrar SWT sería usar la primera opción. Para ello, volvemos a la shell de la aplicación.
En el directorio de la shell de la aplicación, ejecute:
npm install swr
Ahora, modifiquemos el package.json
. Mantenga casi todo, pero modifique la externals
matriz de la pilets
sección de esta manera:
{
"name": "my-app-shell",
"version": "1.1.0",
// ...
"pilets": {
"externals": ["swr"],
// ...
},
// ...
}
Tenga en cuenta que también cambié el número de versión. Como haremos una actualización de nuestro emulador, necesitamos tener una nueva versión. Esto le indicará a Piral que comparta la swr
dependencia con todos los pilotes.
Para probar esto, escribamos npm run build
y publiquemos de nuevo.
npm run build
npm publish dist/emulator/my-app-shell-1.1.0.tgz --registry http://localhost:4873/
Con el shell actualizado disponible, vayamos al directorio de pilet y actualicemos el shell de la aplicación:
npx pilet upgrade
El package.json
archivo del pilet debería haber cambiado. Ahora debería contener una referencia a my-app-shell
en versión en 1.1.0
lugar de 1.0.0
. Además, debería ver swr
listados en devDependencies
y peerDependencies
.
Modifiquemos la página para usar swr
:
import * as React from 'react';
import LogRocket from 'swr';
// note: fetcher could have also been globally configured in the app shell
// however, in general the pilet's don't know about this and so they may want to
// reconfigure or redefine it like here
const fetcher = (resource, init) => fetch(resource, init).then(res => res.json());
export default () => {
const { data, error } = useSWR('https://jsonplaceholder.typicode.com/users/1', fetcher);
if (error) {
return <div>failed to load</div>;
}
if (!data) {
return <div>loading...</div>;
}
return (
<>
Hello {data.name}!
</>
);
};
¡Y ahora hemos terminado! SWR no solo se configura con éxito en nuestra aplicación, sino que también podemos usarlo en todas las micro-frontends. Esto ahorra el ancho de banda para cargar SWR y el caché interno de SWR, lo que brinda a todas las micro-frontends un buen beneficio de rendimiento.
En esta publicación, has visto lo fácil que es comenzar con Piral. Piral te ofrece la opción de distribuir tu aplicación web en diferentes repositorios, incluso entre diferentes equipos.
Solo exploramos la configuración muy básica en esta publicación, pero hay mucho más que puedes hacer con Piral. La mejor manera de explorar Piral es revisar la documentación oficial .
La razón por la que Piral escala mejor que la mayoría de las otras soluciones es que Piral fomenta el acoplamiento flexible. De esta manera, tendrá dificultades para fusionar dos cosas, lo que le ayuda a evitar la superposición de funciones y los monolitos ocultos.
Independientemente de lo que planee hacer, asegúrese de pensar ya en qué dependencias compartir y cuáles dejar en manos del grupo. Hemos visto un ejemplo en el que proporcionar swr
como una dependencia compartida se configuró en segundos. ¡Feliz codificación!
Fuente: https://blog.logrocket.com/creating-micro-frontends-piral/