1. 为什么是微前端:ERP重构不能按照“推倒重来”的逻辑来
先说个背景。我在上一家公司负责一套服务了十多年的ERP系统,规模大概是:前端仓库单仓70个模块、路由表两千多条、样式文件上万行、直接依赖的npm包一百多个。业务覆盖采购、销售、库存、财务、生产、质量、售后,每个模块背后都有相应的业务方。这套系统在当年用了当时比较流行的前后端分离方案,但在今天看来已经非常吃力:一个采购功能的改动可能要编译五分钟,一次发布全量打包产物接近30MB,线上验证要跑两轮回归才能放量。
重构一个大体量ERP,一开始大家都会想到两个方案:一是推倒重来,二是整体迁移到新框架。我先说结论:这两种思路在ERP这种业务复杂度极高的系统里,风险都大到几乎不可操作。推倒重来意味着把十年来沉淀的业务规则全部重写一遍,期间业务不能停、老系统要继续响应需求,两套系统并行维护的成本是双倍的。整体迁移到新框架就更不现实了,因为你根本不可能在有限的时间内把下线的一万多个页面全部验证完。
所以ERP重构的真正解法,不在于“替换掉旧系统”,而在于“让旧系统的一部分可以独立演进、独立发布、独立自治”。这正是微前端架构最擅长的场景。我这次说的“微前端实战”,不是给你讲概念,而是完整记录我们如何把一个单体ERP,逐步拆成多个可独立交付的微应用,并且保证在拆分过程中业务一天都没有停过。如果你手头也有一套“改不动但必须改”的ERP,这篇复盘应该能给你一些实际参考。
微前端在业界的定义很讨巧:它是一种多个前端应用共享同一个浏览器页面、但彼此技术栈无关、生命周期独立的架构模式。注意最后一个词,“生命周期独立”。这意味着每个子应用可以自己决定何时路由、何时加载、何时卸载,主框架只负责承载和调度。
这套思路放在ERP里,对应着一个非常直接的诉求:业务模块之间本来就有清晰的边界,为什么代码层面要强行耦合在一起?
2. 拆什么:业务边界与菜单级拆分策略
2.1 先认清ERP的本质:它不是“一个系统”,是一堆系统的集合
ERP这个名字容易误导人,看起来像是一个整体性很强的企业管理系统,但实际拆开看,它更像是一堆业务系统的集合,只不过共享了同一个登录体系、同一套用户权限、同一个数据库和同一套页面框架。
采购模块关注供应商、采购单、到货、对账;销售模块关注客户、报价、订单、发货、回款;库存模块关注出入库、盘点、调拨;财务模块关注凭证、发票、付款;生产模块关注工单、BOM、工序,这些模块除了共用基础数据和互相调用部分接口之外,几乎不存在真正意义上的“页面级耦合”。
这是一个非常好的微前端拆分前提。因为在微前端领域,最怕的是系统之间边界模糊,拆完之后数据流和事件流纠缠在一起,反而比单体还难维护。ERP恰好相反,它的业务边界天然存在,甚至菜单树本身就是一张拆分图纸,你只需要把“菜单”映射成“子应用”即可。
2.2 拆分粒度的选择:菜单级而不是页面级
拆分的粒度是最容易走极端的地方,我见过有的团队把每个页面都拆成独立应用,拆完之后光浏览器请求就是几十个JS文件,首屏加载慢成灾难;也见过有的团队只把主框架外的两三个大模块拆出来,其余留在老系统里,结果拆分前后体感没区别。
我们最后定的原则是:以菜单树的二级节点为基本拆分单元,按域聚合,偶尔按场景聚合。具体来说是这样的:
- 采购域下的一级菜单“采购管理”,包含采购订单、采购收货、采购退货、供应商协同四个二级菜单,这四个二级菜单合并拆成一个“采购中心”子应用;
- 销售域同理,拆成一个“销售中心”子应用;
- 库存、财务、生产,各自拆成独立子应用;
- 像“个人中心”“系统设置”“用户权限”这类所有业务共用的模块,留在主框架中,不拆。
这种粒度有几个直接的收益。首先,每个子应用的大小适中,编译时间控制在30秒上下,独立发布只需十几秒推一个静态资源;其次,子应用内部的页面间跳转都是无刷新式的,用户体验接近原生单页应用;最后,子应用之间的边界刚好对应了后端微服务的边界,业务归属清晰,出了问题也容易定位。
2.3 先拆“不痛”的模块,再动“核心”的模块
还有一个非常关键的经验,就是拆分的顺序不能按业务重要性排,应该按“风险可控程度”排。
我们是先从“质量追溯”这种流程相对独立、受其他模块影响小的模块开始拆的。这个模块有几张列表和详情页,路由少,接口简单,非常适合作为微前端接入的试点。跑通之后,再逐步接入采购、销售、库存这种核心链路,每一步都有充分验证的时间。
反过来,如果一开始就拆“订单中心”这种被大量模块依赖的公共业务,很容易引发接口稳定性问题、样式回归问题、数据状态不同步问题,一开始就把团队信心打没了,重构大概率半途而废。
3. 技术选型:qiankun、module federation、wujie的实际取舍
3.1 我们考察过的方案
当前微前端技术方案基本分两派。一派是“运行时隔离派”,以qiankun和wujie为代表,核心思路是在运行时把子应用挂载到主应用的容器节点里,通过沙箱机制隔离JS副作用和样式影响;另一派是“构建期共享派”,以Webpack 5的Module Federation为代表,核心思路是在构建时约定全局共享的依赖,子应用打包的时候排除公共依赖,运行时从主应用加载公共模块。
对于一个已经存在多年的ERP项目,最重要的是平滑迁移,而不是追求最新技术栈,所以我们的候选清单里还有iframe方案。iframe的隔离性最好,浏览器原生支持,但通信麻烦,UI同步体验差,加载慢,整体来说更适合做“完全独立的大模块”,不适合做“需要频繁页面级跳转的业务”。
3.2 我们为什么最终选择了qiankun
综合考虑迁移成本、社区生态、开发熟悉度之后,我们用了qiankun。原因有几个:
- 对旧项目侵入小,不需要改动子应用的构建体系,只需要在入口文件里导出几个生命周期钩子;
- 沙箱机制成熟,JS沙箱能拦截子应用对window的修改,样式沙箱能自动给子应用样式加作用域;
- 社区案例多,网上搜ERP微前端重构,绝大多数案例都是基于qiankun的,遇到问题能找到参考;
- 团队熟悉度最高,前后端同学至少都听说过,学习成本低。
对比一下三种方案的核心指标,我整理了一张表:
| 指标 | qiankun | Module Federation | wujie |
|---|---|---|---|
| 技术栈隔离 | 支持(JS沙箱+样式隔离) | 不关心(依赖构建约定) | 支持(JS沙箱+样式隔离) |
| 旧项目迁移成本 | 低,只需改入口文件 | 高,需要应用Webpack5联邦配置 | 较低,引入成本略高于qiankun |
| 部署要求 | 子应用独立部署或和主应用同域部署 | 子应用需要可跨域访问的独立部署 | 子应用需要独立部署 |
| 运行时加载方式 | 主应用动态加载子应用入口HTML | 构建期确定依赖和运行时模块 | 预加载+内存渲染隔离 |
| 社区成熟度 | 较成熟,案例丰富 | 较成熟,但ERP场景案例较少 | 新兴,案例正在积累 |
其实后期我们也实验过wujie,发现在“动态加载外部子应用并做严格隔离”的场景下它的能力也很强,但当时团队已经没有太多精力再去熟悉一套新框架了,所以维持qiankun不动。这里不是铺垫太多理论,我的建议是:如果你的团队已经熟悉了qiankun,不需要为了追新而切换到别的方案;如果你的团队还没有选型,可以看看wujie,它在隔离性和预加载上确实有亮点。
3.3 主框架的承载方式
主框架我们没有单独开发一套管理系统,而是从老系统里把布局框架抽出来,保留顶部导航、左侧菜单、面包屑、用户信息、消息通知这些公共区域,做一个精简版的主应用宿主。它只负责三件事:
- 根据用户角色加载对应的菜单配置;
- 根据路由匹配结果,动态加载对应的子应用入口文件;
- 维护全局登录态和全局UI主题。
主应用自身的业务代码非常少,打包体积大概只有老系统的20%,首屏加载速度快很多,这也为后续整个系统的体验提升打下基础。
4. 运营中心抽离实战:共享态、样式隔离与消息通信
4.1 从零接入一个新子应用的标准步骤
我给一个“标准接入步骤”,这是我们在拆分第二个子应用时沉淀下来的流程,后面所有子应用接入基本都遵循这套:
第一步,在子应用源码根目录新增public-path.js,用于设置webpack的publicPath为动态模式,这样才能保证子应用在任意路径下都能正确加载静态资源。
第二步,修改子应用入口文件,导出qiankun要求的三个生命周期钩子:bootstrap、mount、unmount。在独立运行模式下,入口文件走原来的Vue实例化逻辑;在微前端模式下,mount时才实例化渲染。
第三步,在子应用的webpack配置中设置output.library和libraryTarget: 'umd',这是为了让主应用能通过全局变量找到子应用。
第四步,主应用注册这个子应用,配置entry地址(一般是子应用的HTML地址)、container(子应用挂载容器的DOM节点id)和activeRule(激活路由规则)。
第五步,联调验证:先验证子应用独立运行是否正常,再验证在主应用中通过菜单跳入子应用是否正常,最后验证子应用内部页面跳转、接口请求、退出到主公区是否都正常。
看起来是一套固定的流程,但真正跑起来你会发现每个子应用都有自己的“脾气”。举一个实际的例子,我们的“库存中心”子应用是用Vue3 + Element Plus开发的,而主应用还是老旧的Vue2版本。qiankun并不会帮你自动处理UI库版本冲突的问题,如果你在主应用里已经全局注册了Element UI的按钮、输入框组件,而子应用里又用了Element Plus,页面渲染出来的组件就可能出现双重样式或事件错乱。
4.2 共享态怎么处理:子应用不能直接操作主应用全局Store
微前端里最容易踩的坑,是两个应用之间的“数据共享”。我们在微前端群里见过不少人直接把子应用的Vuex/Redux挂到window上,主应用也去读同一份数据,当时觉得方便,后来线上出了问题,排查一天一夜才发现是两个应用同时修改同一份状态,互相覆盖了。
我们的做法是这样:
- 登录态:由主应用统一维护,通过localStorage写入一个加密的token字段,并封装一个跨应用的
getToken函数。子应用不直接读window里的token,而是从主应用注册好的全局API里获取。这样即使以后token的存储方式变了,子应用也不用改。 - 用户信息:主应用在全局挂载一个
getCurrentUser方法,返回当前登录用户的基本信息。子应用如果需要用户信息,调用这个方法即可,不自己存副本。 - 跨应用公共数据:比如“当前选中供应商”“当前仓库ID”这类业务态,放在主应用的全局状态中,并通过qiankun的
initGlobalState机制广播给所有子应用。子应用监听变化即可。
这套机制跑了一段时间之后,我们总结出一个原则:子应用内部的状态藏在应用自己内部,跨应用的显式状态全部通过主应用转发,禁止子应用之间直接通信。虽然开发的时候会多个两步,但线上稳定性提升非常明显。
4.3 样式隔离的坑:全局样式污染与修复方案
样式隔离是微前端重构中最多人踩坑的地方。qiankun默认给子应用开启的是strictStyleIsolation为false,也就是不开启真正的Shadow DOM隔离,而是采用动态加载和卸载样式表的方式。这意味着,当子应用挂载时,其样式表里的全局选择器(比如直接写在body或html上的样式,以及不带头部类名的组件样式)是有可能影响到主应用和其他子应用的。
我们实际遇到的场景是:旧ERP里有一个全局的table样式,定义了所有表格的边框和字体大小,而采购中心子应用里,Element Plus的表格有自己的样式体系。两个样式串在一起,页面上出现了双线边框、字体忽大忽小的现象。排查起来又痛苦又费时。
最后我们做了两手准备:第一,在子应用自己的样式入口处加了一个统一的#subapp-purchase .el-table这类前缀约束,确保所有子应用样式都被限制在挂载容器内部;第二,在项目代码里全面排查裸的table、input、button全局标签样式,能收敛到命名空间的尽量收敛,不能收敛的改用!important显式覆盖并注明注释。
经验之谈:如果你接手的是一个历史悠久的老ERP,大概率会遇到全局样式满屏幕乱飞的状况,别指望qiankun的样式沙箱帮你挡住一切,规范命名空间才是唯一的长期方案。
4.4 消息通信:自定义事件与qiankun的GlobalState如何配合
ERP系统里不少场景需要跨应用跳转并携带参数。比如在销售订单里打开产品详情页,这个详情页在商品中心子应用里;再比如从库存模块点击“关联采购单”,要跳到采购中心对应的单据详情页。
我们最初的做法是:主应用注册一个全局的navigation方法,子应用通过主应用提供的API跳转,并在query参数里携带业务ID和业务类型。这是一种比较朴素的“URL参数路由模式”,简单但有效,它最大的好处是所有跳转信息都保留在URL中,刷新之后依然能定位到正确的页面。
后来有一些需要传递“非序列化数据”的场景,比如一个对象、一个回调方法,URL参数就没法满足了。这时候我们才引入qiankun的initGlobalState。做法是:主应用创建全局状态,暴露setGlobalState和onGlobalStateChange,子应用在跳转前先把数据set到全局状态,目标子应用在mount阶段注册onGlobalStateChange监听,拿到数据后渲染对应页面。
这里有一个容易犯的错:onGlobalStateChange回调里做的操作要放在“需要的时候”才执行,否则可能会出现这样的现象:打开采购中心做其他操作的时候,因为全局状态被其他模块修改,采购中心里的“当前供应商”突然被切换了,用户一脸懵。
我们最终的解决方案是,在onGlobalStateChange里只更新一个“待处理消息队列”,页面自身在初始化或者切换到某个路由时再去消费这个队列。这样既保证了通信的实时性,也避免了页面被“无形的外部事件”打扰。
5. 老代码迁移路径:从单体重构到渐进式切换
5.1 迁移节奏的三个阶段
老ERP系统不是一个空壳,它有大量存量页面和存量逻辑,不可能一夜之间全部切到微前端架构。我们实际的迁移节奏分成三个阶段:
第一阶段(1~2个月):“改造老系统为可嵌入的主应用”。这一阶段不新增任何业务代码,只把老系统的布局框架抽成主应用,注册第一个试点子应用“质量追溯”,验证整套接入流程和基础能力。
第二阶段(3~8个月):“按域逐步拆离”。每个域独立迭代,每拆完一个域,就把菜单入口从“老系统内嵌页面”切换为“子应用页面”,老系统里对应的路由代码保留一份作为回退方案。这个阶段的核心是保证双轨运行,切错了随时可以回滚。
第三阶段(8~12个月):“老系统瘦身”。所有已拆分子应用的业务代码从老仓库中移除,只保留主应用和未拆分业务的代码,老系统的编译速度和打包体积显著下降。此时,老系统本身已经退化为一个“承载框架+少量未拆业务”的轻量应用,后续可以继续拆或者直接退役。
5.2 路由冲突:老系统路由与新子应用路由如何兼容
这个阶段遇到的最大工程问题是路由冲突。老系统的路由表是集中注册的,路由路径类似/purchase/order/list;而新子应用内部也定义了自己的路由。两个路由体系如果在同一个history下运行,必然出现匹配歧义。
最终方案是:给每个子应用规划一个统一的路由前缀,主应用根据前缀判断是否激活某个子应用,子应用内部的路由全部以这个前缀为根。比如采购中心的前缀是/purchase-center,那采购订单列表的真实路由就是/purchase-center/order/list。
听起来很顺,但落地时有很多细节:
- 主子应用如果都用
createWebHistory,需要统一mode,否则主应用用history、子应用用hash,切换的时候URL会混乱; - 子应用在独立运行时需要用根路径前缀
/purchase-center访问,这要求子应用的开发服务器也支持history回退; - 主应用在注册子应用路由时,
activeRule里的匹配规则需要排除掉一些公共页面(比如登录页、404页)。
这些在纯粹开发子应用时都不用考虑,只有在微前端接入时才会暴露出来。我建议在拆分前就把路由前缀规范定好,写在团队规范文档里,否则每个人自己起一套路由,后面合到一起非常痛苦。
5.3 双轨运行时的灰度发布与回滚方案
“拆完直接全量切”是重构的大忌。我们在每个子应用接入后,都采用了一套灰度切换策略:
- 先在内网环境完整走一遍业务流程,包含正常单据和异常单据;
- 再让一个核心客户(或一个分支区域)定向体验,仅把该客户所属用户的菜单入口指向子应用,其他用户仍然走老系统页面;
- 定向体验通过后,再按比例放量,比如先10%用户,再30%、50%,最后100%;
- 10%用户如果出现线上Bug,立即通过配置中心把菜单入口回退到老系统,整个过程不需要发版,改配置即可。
为了保证双轨运行期间体验一致,我们在老系统页面和新子应用页面的URL参数层面做了接口兼容设计,前端传参和接口名都保持统一。这样回滚的时候,用户无感知,数据也不会有两条链路。
6. 拆分之后的性能与工程化:懒加载、依赖去重、发布节奏
6.1 子应用体积和首屏优化的实际数据
拆分前,老系统单次打包产物约为29MB。拆分后,主应用首屏需要加载的资源大概7MB,包含第三方库和公共样式;每个子应用按需加载,典型的子应用包体在2~4MB之间。
这里有个细节:qiankun默认是在“第一次激活子应用时才去加载它的入口HTML和JS资源”,也就是说首屏用户只会加载主应用,不会加载其他子应用。这对于ERP这类有大量业务模块的系统来说非常划算,用户打开首页也明显变快了,从原来的3~5秒,降到1~2秒。
实测的数据:
| 场景 | 重构前 | 重构后 |
|---|---|---|
| 首屏加载时间 | 约3.8s(缓存后约2.5s) | 约1.6s(缓存后约0.9s) |
| 首屏请求大小 | 29MB(全量包) | 主应用7MB(后续子应用按需加载) |
| 编译时间(全量) | 约5分钟 | 主应用30秒,子应用单独编译20~40秒 |
| 发布时长 | 全量发布约10分钟 | 子应用独立发布约1分钟 |
这些数据在不同机器、不同网络环境下会有差异,但总体趋势非常明显:微前端拆分不是增加了复杂度,反而通过按需加载、独立编译这些机制,把原来的“大而慢”变成了“小而快”。
6.2 公共依赖去重:到底该不该抽公共包
维护公共依赖是微前端工程化里的一个选择题。如果每个子应用都把Vue、Element Plus、axios打进去,那么用户进入多个子应用时,浏览器会重复加载这些库,浪费流量也拖慢切换速度。
我们的方案是:把Vue全家桶(包括Vue、Vue Router、Vuex、Axios)以及公司自研的业务SDK作为公共依赖,在主应用通过externals排除,子应用通过externals同样排除,然后在主应用的index.html里通过CDN统一引入。这样多个子应用共享一份公共依赖,切换子应用时浏览器缓存都能命中。
但要不要把Element UI也抽出去,我们犹豫了很久。抽出去的好处是包体变小;但坏处是,如果有的子应用还在用Element UI 2.x,有的要升级到Element Plus,公共依赖就不好统一了。最终我们选择:样式类组件库留在各自子应用里,只在主应用保留最基础的布局组件。这算是速度和迁移成本的折中。
6.3 发布节奏:子应用按域发布,主应用按周发布
ERP有个现实问题:不同业务域的发布窗口不一样。财务域因为涉及月结,月中不能随便发版;生产域的发布经常要配合车间的生产计划,不能在工作日白天直接发布;销售域则希望快速迭代,每周都能上线新功能。
单体架构下,所有模块的发布节奏绑在一起,任何一个域不能发版,其他域也别想动,这是一件非常憋屈的事。微前端拆分之后,每个子应用由自己的业务团队独立发布,主应用只承载框架和公共能力,发布频率降到了每周一次甚至两周一次。遇到紧急问题,子应用可以直接发布回滚,不需要等主应用时间窗口。
这个变化带来的团队组织层面收益,甚至比技术层面的获益更明显:大家不用再互相等待了,发布这件事从“全部门排队”变成了“各司其职”。
7. 踩坑清单:样式污染、全局变量、路由冲突、沙箱机制
7.1 qiankun沙箱的边界在哪里
qiankun的沙箱机制经常被误解成“全隔离”,但实际上它有一块经典的灰色地带:非原生对象的全局变量劫持。
比如某个子应用里定义了一个window.someVariable,qiankun的沙箱理论上会在子应用卸载时把这个变量清理掉;但如果这个变量是通过window.x = { nestedObj }然后修改nestedObj内部属性的方式写入的,沙箱在某些情况下只能拦截到表层,内部对象引用仍然有可能泄漏到全局。
我们在拆分采购中心时候就遇到过这种问题:一个采购列表页面运行完卸载后,又进入库存中心页面,结果库存页面里莫名其妙多了一个window._purchaseLastQuery变量。排查发现是采购中心的某个组件在beforeDestroy里没有清理定时器,而定时器回调里尝试写入一个全局引用,被沙箱放过了。
这件事让我们意识到:不能把沙箱当保险箱,子应用里所有对全局变量的写入,都应该通过显式API来管理,不要一边写一边指望沙箱帮你擦屁股。
7.2 路由切换时的卸载遗漏
微前端最典型的一个问题:从一个子应用切换到另一个子应用时,前一个子应用没有被正确卸载,DOM残留、事件监听残留、定时器残留。表面现象是:切换之后页面上的弹窗还在闪,或者控制台一直报“Cannot read property of undefined”。
我们写了一个检测脚本:在每次切换子应用前,通过console.log打印出当前子应用根节点下还有哪些DOM节点和事件监听器。这个脚本在联调阶段帮我们定位了大量卸载不干净的问题,后来它被保留下来,每次发布前都会跑一遍。
值得提醒的是,unmount生命周期里要做的事情不能只写“销毁Vue实例”,还要把子应用创建的全局定时器、事件总线监听、echarts实例、全局通知组件全部释放掉。这些细节在单独开发子应用时根本不会注意,但在多个应用交叉运行的微前端环境里,任何一个遗漏都可能造成内存泄漏。
7.3 跨子应用跳转时的URL状态丢失
我们的销售中心和采购中心经常需要互相跳转,而且跳转时要带上“当前组织单位ID”“当前业务员ID”这类上下文参数。如果用query传参,当URL很长、嵌套很深时,浏览器历史记录会变得又长又乱,而且一旦用户刷新页面,前面的上下文参数如果依赖前一个页面构造,就会丢失。
最终我们约定了一套“重定向中心”方案:所有跨子应用跳转,统一走主应用的一个路由处理函数,函数内部拼接标准的路由前缀和目标页面路径,并把上下文参数编码后放到query里。目标子应用的页面在初始化时解析query,如果发现有上下文参数,就自动加载对应数据并恢复页面状态。
这套方案已经稳定运行很久了,效果很好。但是也要强调,这个方案的适用前提是“页面之间传递的数据都是可序列化的”。如果是复杂对象或者函数引用,那还是老老实实用GlobalState。
8. 演进方向:从qiankun(存量迁移)到模块联邦(增量共建)
文章写到这里,很多朋友可能会有疑问:既然qiankun这套运行时隔离机制已经能满足需求,为什么还需要关注Module Federation?我当时的判断和后来的实践是:这两者面向的其实是“存量”和“增量”两个阶段。
qiankun解决的是“老系统怎么接入新架构”的问题,它最适合存量系统的渐进式重构。我们花了近一年时间做的是这件事。但是在老系统稳定下来、新系统开始成规模发展之后,新的问题又出现了:多个新子应用之间,需要共享大量的基础组件和工具函数,如果每次都是发布到npm然后各自安装,版本同步又成了新的维护负担。
Module Federation的价值在这个阶段开始体现。它可以让一个子应用作为“提供方”,把“基础组件库”“公共业务组件”和“共享的工具集”直接暴露给另一个子应用,运行时从提供方动态拉取。这样公共部分的迭代不再需要发npm包,也不需要全部子应用跟着发包,只要提供方的服务在线,所有消费方都能拿到最新版本。
我们目前正在做的是:qiankun继续承担老系统的接入和稳定承载,Module Federation则用于新系统内部多个业务应用之间的组件共享。两套机制共存其实没问题,因为它们解决的是不同层级的问题。主应用通过qiankun动态加载子应用,子应用之间通过Module Federation共享模块,各司其职。
当然,这并不是唯一的演进方向。如果你的团队是全新项目从零开始,完全可以一上来就用Module Federation做构建期共享架构,省掉运行时沙箱那部分的复杂度;如果你的业务领域特别强调隔离,那wujie在运行时隔离上做得更彻底。选哪种方案要结合你的团队基建和业务现状,没有放之四海而皆准的答案。
我个人的体会是,ERP重构这件事情,与其说是一次技术升级,不如说是一次系统性基建的“换血”。它不是单纯地把一个老框架换成新框架,而是要把“所有模块绑定在一起发布”的积弊,逐步解耦成“每个业务域独立演进”的健康状态。微前端只是这个过程中的一种工具,真正决定成败的,还是拆分边界是否清晰、迁移节奏是否合理、团队是否建立起了新的工程习惯。
最后再分享一个小技巧:如果你所在的团队对微前端还没有太多把握,可以先把“登录态统一”和“菜单动态化”做成主应用的两个核心能力,这几乎是所有ERP类系统都具备的基础设施。把这两个能力打通了,后续接入任何子应用都会顺畅很多——它们也是整个重构过程中最底层、最不能出错的两个环节。