第一部分:创建host应用程序
我们的目标是基于 single spa 和模块联邦构建一个示例微前端解决方案。为了实现这一目标,我们将开发以下内容:
- single spa 根配置
- 用于微前端与主机应用程序以及其他微前端进行通信的发布/订阅消息代理。
- 基于 MobX 的全局存储,用于在微前端之间共享一些与 UI 相关的数据。
为了使解决方案更加可行,我们将为微前端创建一个 nx 管理的 monorepo。
在我开始code时,single spa 已经保持了其主要功能已经很长一段时间,包括创建主机应用程序,或者称之为“root-config”。
要创建一个 root 配置,只需执行:
npx create-single-spa
选择应用程序类型为 root-config,您可以选择使用其布局引擎与否。在我的项目中,我没有将其添加到项目中。
root-config.ts 文件是最重要的文件,因为您可以在此文件中添加大部分主机应用程序逻辑。
在我的案例中,在构建路由之前,需要进行基于 WIFI 的身份验证。主机应用程序将尝试向身份验证服务器发送请求并获取一组令牌,其中一个是所有即将进行的 ajax 请求的 access_token,另一个是在 access_token 过期时使用的 refresh_token,refresh_token 将用于重新验证用户身份并交换另一组令牌。关于身份验证流程,我们可以在另一系列的博客文章中进行扩展。好的,我们假设身份验证流程在一个名为 authenticate 的异步函数中完成:
const authenticate = async (authURL: string, requestBody): Promise<string | null> => {
return new Promise((resolve) => {
fetch(authURL, {
method: "POST",
body: requestBody,
})
.then((res) => {
if (res.status !== 200) {
throw new Error("401");
}
return res.json();
})
.then((res) => resolve(res.tokenGroup))
.catch(() => resolve(null));
});
};
当身份验证完成时,我们构建路由,否则显示一个简单的错误页面:
const url = ''; // 替换为您的 URL
const user = {
username: '',
password: ''
};
const tokenGroup = await authenticate(url, JSON.stringify(user));
if (!tokenGroup) {
registerApplication({
name: "error-app",
app: {
bootstrap: async () => {},
mount: async () => {
const template = `
<div id="root-error">
<h1 id="root-error_title">Error</h1>
<p id="root-error_content">There is no valid token found, please check your network connection</p>
</div>
`;
document.getElementById("root").innerHTML = template;
},
unmount: async () => {},
},
activeWhen: ["/"],
});
start({
urlRerouteOnly: true,
});
return;
}
const { access_token } = tokenGroup;
// 处理access_token
// ...
然后,我们需要构建自己的路由:
const getRoutes = (loadAppMap) => {
let mainApps = "";
for (const app in loadAppMap) {
const [url, appName] = app;
mainApps += `<route path="/${url}"><application name="${appName}"></application></route>`;
}
return `<single-spa-router base="/" containerEl="#main">${mainApps}</single-spa-router>` as string;
};
在拥有路由之后,注册我们的应用程序:
jsxCopy code
const initApps = (authorizedApps) => {
const template = await fetchConfigAndConstructTemplate(authorizedApps);
const routes = constructRoutes(template);
const applications = constructApplications({
routes,
loadApp({ name }) {
if (loadAppMap[name]) return System.import(loadAppMap[name]);
return System.import(name);
},
});
const layoutEngine = constructLayoutEngine({ routes, applications });
applications.forEach(registerApplication);
layoutEngine.activate();
addErrorHandler((err) => {
const errorStatus = getAppStatus(err.appOrParcelName);
if (errorStatus === LOAD_ERROR) {
unloadApplication(err.appOrParcelName);
System.delete(System.resolve(err.appOrParcelName));
}
});
start({
urlRerouteOnly: true,
});
};
注意:上面的代码由于道义和公司政策的原因进行了更多或更少的修改,它只是帮助您了解设置的一种方式,而不是一个可以直接使用的应用程序。下一次,我们将在我们这次创建的应用程序中创建一个 nx monorepo。