商家后台前端代码目前代码量达到十万级,每个迭代团队需要在同一仓库中迭代几十个需求,在日渐庞大的巨石应用下如此活跃的迭代,开发效率与构建效率上给我们带来了一些挑战,我们需要优化以下几点:
微前端是目前解决应用拆分的主要解决方案,但是由于其隔离性的机制使得各个子应用间完全隔离,使得用户在开发子应用时无法访问其他子应用页面,这对于各子应用存在关联关系需要同时访问开发的场景开发效率较低,并且目前市面上已经完全封装的主流微前端框架对我们来说是黑盒,无法做到高度自定义,无法满足特定拆分需求,因此我们决定采用模块联邦与大仓模式结合的方式解决以上问题。
将巨石应用进行拆分,独立开发与部署、提升构建部署效率与代码维护效率。
由于商家内部各应用间在业务上存在上下游的关系,我们在开发D子应用时,需要在其上游的C、B子应用中进行相关配置,它们之间是强绑定的关系,结论就是:需要在本地开发时,能够同时使用其他子应用的页面模块的功能。
以上提到的提升构建与维护效率,目前微前端与MF方案都是支持的,关键点在于如何在本地开发能够访问到平台的全量功能。本地代码已经有了,启动本地进程即可,那么,其他子应用如何访问?
如果是从本地再启动其他应用,这其实就背离了应用拆分的初衷,同时也降低了本地开发的效率,同时随着子应用越来越多,本地进程也会越来越多,这可能会大大影响本地开发效率。看来本地启动行不通,那么从哪里访问呢?对了,线上不是可以访问到其他模块吗?但是怎么将本地开发与线上的模块结合呢?由于目前商家后台使用了MF方式在路由层或vue文件内远程引入MF模块,那么是否可以通过MF模块转发的方式将远程组件代理至本地组件呢?
以上的组件代理插件实质上是用于做MF组件加载管理的chrome插件,可以控制基座应用加载的模块是线上的还是本地。
代理方案如下
下面是具体实现:
generateRedirectUrl = (details: UrlDetailsType) => { if (details.url.includes(MICRO_ONLINE_LOAD_PATH)) { const redirectUrl = this.generateDefaultProxyUrl({ originUrl: details.url }) if (redirectUrl) { console.log('触发代理', `${details.url}代理至${redirectUrl}`) this.checkMicroAppStatus({ originUrl: details.url, redirectUrl }) return { redirectUrl, } } } if (details.url.includes('t1-dev.dewu:98')) { console.log("details.url.includes('t1-dev.dewu:98')", details.url); const redirectUrl = this.generateOnlineUrlByLocal({ originUrl: details.url }) if (redirectUrl) { console.log('触发代理', `${details.url}代理至${redirectUrl}`) this.checkMicroAppStatus({ originUrl: details.url, redirectUrl }) return { redirectUrl, } } } }
function getHost() { if (process.env.SOCKET_SERVER) { return new URL(process.env.SOCKET_SERVER); } return location;}function getSocketUrl() { let h = getHost(); let host = h.host; host = `localhost:${PORT}`; const isHttps = h.protocol === 'https:'; return `ws://${host}`;}function getPingUrl() { const h = getHost(); return `${h.protocol}//${h.host}/__umi_ping`;}
let pingTimer = null;let isFirstCompilation = true;let mostRecentCompilationHash = null;let hasCompileErrors = false;let hadRuntimeError = false;const pingUrl = getPingUrl();if (!window[`${APP_NAME}UmiEntry`]) { const socket = new WebSocket(getSocketUrl(), 'webpack-hmr'); socket.addEventListener('message', ({ data }) => __awaiter(void 0, void 0, void 0, function* () { data = JSON.parse(data); if (data.type === 'connected') { console.log(`[webpack] connected.`); // proxy(nginx, docker) hmr ws maybe caused timeout, // so send ping package let ws keep alive. pingTimer = setInterval(() => socket.send('ping'), 30000); } else { handleMessage(data).catch(console.error); } }), );}
前面的代理机制依赖于MF的远程加载,模块联邦加载机制可参考「掘金」平台中题目为“最详细的Module Federation的实现原理讲解” 这篇文章。基于模块联邦的微前端落地方案可以参考之前的一篇文章 基于Module Federation的模块化跨栈方案探索。
本地与部署态基座应用通过MF方案加载子应用,同时部署态新增动态加载保证远程组件的实时性,在加载入口文件处进行监控告警。
基座应用为部署态,在进行MF加载时,通过chrome插件动态控制加载子文件路径,开发态子应用共享部署态代码的store,路由注册等基础配置。
加载态依赖chrome插件做动态代理,实现本地与其他测试环境构建代码的动态切换,同时子应用与部署态代码建立websocket代码更新链接,在子应用更新代码时,实时刷新线上页面。同时支持端口的动态配置,一键关闭。
以上介绍加载链路保证了构建部署提速与功能的完整,较好的解决了应用拆分功能不完备问题。本次架构优化将构建由15s减少至2.0s。业务需求部署速度由8min减少至2min。
应用拆分只是目的,要实现这个目标不仅仅要做拆分,对于商家后台来说各个应用间的复用同等重要,由于是业务解耦,这意味着各应用间存在更多可复用的功能与模块。
同时不仅是商家后台的部分模块也会在交易后台使用,既要保证应用业务的解耦,同时要保证组件充分复用,大仓模式是目前最合适的方案。
由于商家后台各个子应用由于同属商家整条业务链路,存在众多可共用的组件和模块,而npm发布模式本身给业务组件与业务项目带来了一定隔离性,同时因为各子应用业务上存在关联,很多大型模块需要被多个子应用引入,而这些大型模块的迭代通常比较频繁,同时需要对业务请求进行封装。这里我们使用了基于大仓模式的源码引入以达到代码共用的目的。组件开发链路如下:
这里体现的是源码引入的方式,在构建态进行通用模块的打包构建,这一点目前能跑通的背景是商家后台本身是一个完整的应用,现有的模式同样是一个组件被多个模块所使用,同时测试阶段也是全量回归。以下是大仓组件基础链路:
之后会对该部分做详细介绍。
以上主要讲述了MF方案如何将本地结合线上开发,这里仅对微应用级别做了解耦,基于MF的模块化实现,由于remoteMicro实质上是创建了一个引用路径到require函数的映射然后代理至本地,那么对于不同模块,在能力上是具备模块化代理的能力的,基于目前MF按需构建(仅构建暴露出去的组件模块)的规则,我们可以对某个模块的映射对象里的xxx.async.js做代理。这样就可以实现页面粒度的按需构建,在部署构建提速上有很大潜力。
本篇文章主要介绍了如何对商家巨石应用做拆分,包括拆分方案的介绍,如何同时保证单个构建与功能完整性,并且针对微应用代理加载进行了进一步探索,接着介绍了大仓模式下需要遵循的规范以及未来的规划。大仓模式目前在前端平台已经持续不断地完善,将来应该会针对此模式做更详细的介绍,在拆分这件事情上,对于构建本身或许能被更加细粒度化,构建文件的代理本质上减少了代码的构建量,目前是通过人为控制的方式,此次验证了模块联邦支持可代理与动态更改expose。基于这两个特性,是否能将构建做到更加局部化,这可能会成为构建优化的方向。应用拆分一方面提升了开发人员的开发与部署效率,同时也对业务迭代流程做了业务解耦,明确了责任边界,更有利于后台应用的开发需求管理,降低需求代码维护成本。
本文链接:http://www.28at.com/showinfo-26-6162-0.html基于模块联邦与大仓模式的商家巨石应用拆分实践
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com
上一篇: 转转游戏的账号订单流程重构之路
下一篇: 如何编写技术文档?