如何在子应用回滚时保持基座无需重启
解读
在微前端架构里,基座(主应用)负责统一路由、权限、公共资源,而子应用独立构建、独立部署。当线上子应用出现缺陷需要“秒级回滚”时,若每次回滚都要求重启基座,会导致所有子应用短暂不可用,违背“故障隔离、增量发布”的设计初衷。
国内主流方案(qiankun、micro-app、无界)都基于运行时加载而非“编译期打包”,因此回滚的关键是让基座在运行态拿到旧版本子应用,而不重启自身进程。
面试官想考察:
- 是否理解微前端“生命周期钩子 + 运行时加载”的本质;
- 是否能在 Grunt 构建层把“版本化、差异化、可回滚”做进流水线;
- 是否熟悉国内云原生发布习惯(蓝绿、金丝雀、OSS + CDN 秒级切换)。
知识点
- 子应用版本化构建
Gruntfile 中通过grunt-contrib-uglify、grunt-filerev、grunt-usemin把子应用打出带 hash 的入口 bundle(如app.2c3e4f5a.js),并同步生成manifest.json记录版本映射。 - 非覆盖式发布
将产物按“版本文件夹”上传 OSS:/app-a/1.0.11/、/app-a/1.0.10/;CDN 路径永不覆盖旧文件,实现“秒级回滚只需切路径”。 - 运行时路由与钩子
基座在registerMicroApps时读取远端配置中心(Nacos、Apollo、自建配置服务)拿到entry字段,如//cdn.xxx.com/app-a/1.0.10/index.js;回滚即改配置,基座下次加载子应用自动取旧入口,已挂载的子应用保持不动。 - 沙箱清理与内存泄漏防控
回滚前主动调用unmount生命周期,清除 window 污染、事件监听、定时器;qiankun 的proxySandbox在卸载时自动重置全局变量,防止旧代码残留。 - 灰度与优雅降级
配置中心支持用户维度灰度:cookie 里带app-a-version=1.0.10的优先回滚,其余用户仍走 1.0.11;基座侧通过fetch兜底策略,若新入口 5s 未响应自动降级到上一稳定版本。 - Grunt 自动化回滚流水线
新增grunt-task-rollback:- 调用 OSS API 把
/app-a/1.0.10/的x-oss-object-acl设为public-read; - 调用配置中心 OpenAPI 把
app-a.entry字段改为 1.0.10 的 CDN 地址; - 通过
grunt-webhooks触发钉钉/飞书机器人通知。
全程无需重新构建、无需重启基座,平均耗时 3~5 秒。
- 调用 OSS API 把
答案
- 构建阶段:Gruntfile 把子应用打出带 hash 的入口文件,并按版本号上传到 OSS 非覆盖式目录。
- 发布阶段:基座通过配置中心动态获取子应用 entry,而不是写死路径;首次加载时把入口脚本缓存在IndexedDB + LRU,减少二次网络请求。
- 回滚阶段:
a. 运维在 Grunt 流水线执行grunt rollback:app-a:1.0.10;
b. 任务脚本把配置中心对应 entry 字段改为 1.0.10 的 CDN 地址;
c. 基座监听popstate或路由切换时,发现子应用需要重新加载,则拉取旧版本入口;当前已挂载的子应用实例保持运行,直到下一次路由切换才卸载,实现“热回滚、零重启”。 - 兜底策略:若新入口加载失败,基座自动降级到上一版本;同时上报 Sentry,Grunt 侧定时任务 5 分钟内禁止再次发布该子应用,防止“滚挂”。
拓展思考
- 多子应用并行回滚:当一次发布涉及 app-a、app-b 两个子应用,如何在一次 Grunt 任务里做“事务级回滚”?
思路:配置中心支持批量事务接口,Grunt 任务先把两个子应用的 entry 统一写入一个rollbackTicket,成功后再原子提交;若任一失败则整体回滚,保证一致性。 - 子应用样式污染:旧版本 CSS 也可能有缺陷,回滚时如何确保样式也同步还原?
方案:CSS 同样按版本文件夹存放,并在子应用mount时动态插入<link>,卸载时移除;借助 ShadowDOM + CSS Modules 可彻底隔离,但需权衡浏览器兼容性。 - 本地开发如何模拟回滚:
在 Grunt 里起两个 dev-server,端口 7100(v1.0.11)、7101(v1.0.10),通过grunt-contrib-proxy把基座请求转发到不同端口,前端开发者无需重启基座即可一键切换版本,提前验证回滚逻辑。