启动所有子应用 + 主应用

GC观察员
2026-01-04 11:58
阅读 253

微前端落地实战:我在深圳搞大型项目拆分的血泪史

大家好,我是阿哲,普通一本CS专业的大四狗,已经拿下了深圳某鹅厂系公司的offer,现在处于等入职前的“养老期”。不过别误会,我可不是躺平——去年秋招后就被拉进一个内部孵化项目打杂,没想到阴差阳错成了微前端架构的“小白鼠”。

事情是这样的:我们团队负责一个面向企业客户的SaaS平台,前端代码库已经有三年历史,主应用臃肿到连 git status 都要卡两秒。产品经理动不动就说“这个模块下周上线”,结果开发改个按钮颜色都要全量构建、全量部署,测试同学天天在群里@我:“你这改的是登录页,怎么把报表页干挂了?”

痛定思痛,技术负责人拍板:上微前端!但没人真正做过,文档看一堆,GitHub 上 star 最高的 qiankun 也只敢在 demo 里跑。我这个“即将入职的应届生”被临时抓壮丁,美其名曰“培养新人”,实则是没人愿意踩坑(笑)。


为什么是微前端?不是组件库?

一开始我也疑惑:为啥不直接搞个共享组件库 + monorepo?后来发现根本行不通。
我们的系统有四个核心业务线:客户管理、订单中心、数据分析、权限控制,每个团队独立迭代,甚至用的技术栈都不同——主应用是 Vue2,数据分析那边偷偷上了 React 18,还有个老模块还在用 jQuery(别问,问就是“祖传代码不能动”)。

这时候微前端的优势就出来了:解耦部署、独立技术栈、按需加载。我们不需要统一框架,只要约定好通信机制和样式隔离,就能让各个子应用像“插件”一样热插拔。


踩坑一:乾坤(qiankun)真的能“千坤”吗?

我们选型时对比了 single-spa、micro-app 和 qiankun。考虑到文档完整性和国内社区活跃度(腾讯系内部也有不少项目在用),最终选了 qiankun。

但上手第一天就翻车了。子应用打包后资源路径全错,主应用加载时报 404:

GET http://localhost:8080/js/chunk-vendors.js 404 (Not Found)

查了半天才发现,Vue CLI 默认的 publicPath/,而微前端要求子应用必须知道自己的“挂载基路径”。解决方法是在 vue.config.js 里动态设置:

// vue.config.js
const { name } = require('./package.json');

module.exports = {
  configureWebpack: {
    output: {
      library: `${name}-[name]`,
      libraryTarget: 'umd',
      jsonpFunction: `webpackJsonp_${name}`,
    },
  },
  devServer: {
    headers: {
      'Access-Control-Allow-Origin': '*',
    },
  },
  // 关键!动态 publicPath
  chainWebpack: config => {
    config.output.set('publicPath', 'auto');
  }
};

另外,本地开发时主子应用端口不同,跨域问题也得处理。我们直接在 devServer 加了 CORS 头,简单粗暴但有效。


踩坑二:CSS 样式污染,谁动了我的按钮?

最恐怖的一次线上事故:数据分析团队更新了一个全局 .btn 样式,结果把主应用的提交按钮变成了粉色圆角——客户投诉说“你们系统变少女风了?”

微前端的样式隔离必须做!qiankun 提供了两种方案:沙箱隔离(experimentalStyleIsolation)Shadow DOM。我们试了前者,但性能损耗明显(尤其在低端安卓机上),最终采用 CSS Module + BEM 命名规范 强制约束,并配合构建时注入唯一前缀:

/* 子应用 order-center 的按钮 */
.order-center__submit-btn {
  background: #007aff;
}

同时,在主应用加载子应用时,我们用 fetch 预加载 CSS 并动态插入 <style> 标签,避免 FOUC(Flash of Unstyled Content)。用户体验这块,不能将就。


踩坑三:父子应用通信,别再用 localStorage 了!

早期有人提议用 localStorage + storage 事件传数据,我当场表演一个瞳孔地震——这玩意儿同步不可靠、调试困难、还容易被清理。

我们最终采用 qiankun 的 props 透传 + 全局状态管理(基于 RxJS 的轻量 Store)。主应用初始化时注入公共信息:

// 主应用
loadMicroApp({
  name: 'order-center',
  entry: '//localhost:8081',
  container: '#subapp-container',
  props: {
    user: currentUser,
    theme: currentTheme,
    eventBus: globalEventBus, // 自定义事件总线
  }
});

子应用通过 props 拿到 eventBus,即可订阅/发布消息:

// 子应用
export async function mount(props) {
  props.eventBus.on('themeChange', handleTheme);
}

这种方式既解耦又可控,比瞎搞 window.xxx 安全多了。


性能优化:别让微前端变成“微卡顿”

微前端最大的性能陷阱是重复加载公共依赖。比如主应用和子应用都用了 Lodash,结果用户要下载两份。

我们的解决方案:

  1. 主应用提供共享依赖:通过 Webpack 的 externals 把 Vue、Lodash 等暴露到 window
  2. 子应用按需引入:子应用配置 externals,直接引用全局变量
// 主应用 webpack
externals: {
  vue: 'Vue',
  lodash: '_'
}

// 子应用 vue.config.js
configureWebpack: {
  externals: {
    vue: 'Vue',
    lodash: '_'
  }
}

实测首屏加载时间从 4.2s 降到 2.1s,Lighthouse 分数提升 30+。

为了更直观,整理了个对比表:

指标 单体应用 微前端(初期) 微前端(优化后)
首屏加载 2.8s 4.5s 2.1s
JS 体积 3.2MB 4.7MB 2.9MB
独立部署
技术栈自由

工具链和文档:别让新人两眼一抹黑

项目中期,新来的实习生看了三天代码还是不会本地联调。于是我们做了两件事:

  1. 写了一份 《微前端开发手册》(其实就是 Notion 页面),详细说明如何启动主/子应用、如何调试、常见报错
  2. 在 GitHub 仓库根目录放了 DEVELOPMENT.md,一键脚本搞定环境
npm run dev:all

另外,强烈推荐搭配 Vite + qiankun 插件,HMR 快得飞起。虽然我们项目因为历史原因还在用 Webpack,但新子应用已经全面 Vite 化。


最后一点真心话

微前端不是银弹。如果你的项目只有两个页面、一个团队维护,千万别为了“高大上”硬上。它解决的是组织架构复杂性带来的工程问题,而不是技术炫技。

我自己也是边学边干,期间翻烂了《微前端实战》这本书(机械工业出版社那本),还扒了 GitHub 上十几个开源项目源码。有时候凌晨两点盯着控制台报错,真想删库跑路。但当看到四个子应用独立部署、互不影响地跑在线上,那种成就感——值了。

现在我已经把这段经历写进了简历,面试官问起时还能吹两句“架构设计”。等入职后,说不定还能给新团队安利这套方案(前提是别遇到祖传 jQuery 项目 😅)。

共勉。

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝