
简介本资源是一个面向前端开发者特别是Vue技术栈工程师的MicroAPP微前端开发实践项目旨在解决传统Vue项目接入微前端架构时的适配难题。通过封装为Vue CLI插件该项目提供开箱即用的MicroAPP运行时注册、生命周期管理、样式隔离与通信机制支持显著降低微前端落地门槛。压缩包共18个文件336KB包含10个核心JS文件如registerWebpackPlugin.js、chainConfig.js、service层逻辑、2个配置类JSONpackage.json、micro-app.config.js、2个Markdown文档README.md、CHANGELOG.md、1个HTML入口页及LICENSE等工程必需文件结构清晰便于理解插件注册原理与构建链路定制逻辑。目前已有46人学习下载读者可直接复用该插件快速搭建符合MicroAPP规范的子应用掌握Vue CLI插件开发范式、微前端沙箱集成要点及CI质量保障实践含Jest单元测试与CircleCI配置。 如果你和我一样项目从单体 Vue 应用拆成十几个子应用之后第一件事不是欢呼而是找一种能让所有人同时上线、还不互相打架的微前端方案那你大概率会把 single-spa、qiankun、micro-app 的文档都翻个遍。这篇要拆解的源码项目名字叫“基于Vue CLI插件的MicroAPP项目”核心思路是把 micro-app 的接入过程封装成一个 Vue CLI 插件让主应用和子应用的生成都被收敛成一条脚手架命令。我在自己团队里用这套模板搭过三个中后台系统整套流程比较顺手所以这篇会把源码里值得关注的设计、能直接抄走的配置以及我踩过的坑都拆开讲。这个内容适合两类人一类是打算在 Vue CLI 技术栈里引入微前端但卡在配置细节上的前端另一类是团队里的工程化负责人想把微前端的接入做成一套统一模板不想每个项目从零开始手工改配置。下面不绕弯子直接进入正题。1. 为什么我在 Vue CLI 工程里会看中 micro-app 这套方案1.1 single-spa、qiankun 都没让我安心的原因我在实际项目里先试用的是 single-spa。它其实更像一个“应用加载规范”所有路由切换、生命周期管理都要自己实现。你需要把子应用改造成导出 bootstrap、mount、unmount 三个函数还要引入 SystemJS 或者自己维护一份应用注册表。如果团队里大多是传统的 Vue 2 Vue CLI 项目这种改造会让老项目的维护者非常抵触。因为一个本来只是new Vue()挂载到#app的应用改成生命周期函数之后不能直接独立运行还得处处提防全局变量被沙箱影响。在拆业务的时候光是让每个子应用的构建产物兼容 systemjs就要花不少精力去处理 webpack 的libraryTarget和 externals。qiankun 在 single-spa 之上做了很多简化把 JS 沙箱、样式隔离、应用通信都封装好了但 qiankun 对子应用的侵入并没有消失只是藏得深了。子应用依然要暴露生命周期vue.config.js里的library和libraryTarget要配合调整history 路由的base也得处理。我在 qiankun 上遇到的问题最典型的是打开页面子应用白屏控制台报变量名被 webpack 打包后压缩导致的作用域冲突最后要把libraryTarget: umd配合特定 externals 才不报错。这类问题网上方案很多但每个项目情况不一样排查起来还是费劲。1.2 micro-app 的 WebComponent 思路为什么更适合 Vue 团队micro-app 的思路和前面两者差别很大。它基于 WebComponent 的 custom element你只需要在基座应用里写一个micro-app标签把子应用的地址当成url属性传进去剩下的事情由框架内部处理。它会把子应用的 DOM 渲染到这个自定义元素内部同时执行子应用的 JS并通过 shadow DOM 或自定义隔离机制防止样式互相污染。这种思路比较微妙的地方是它既不像 iframe 那样把子应用放在一个完全独立的容器里导致通信、尺寸、弹窗都很别扭也不像 single-spa 那样要求你把应用改造成一个模块。子应用该new Vue()还是new Vue()该挂载还是挂载基本是“原来怎么写现在还怎么写”。对 Vue 技术栈的团队来说这种接入成本的下降是立竿见影的。而且我不需要专门为子应用做一层打包改造Vue CLI 构建出来的普通代码就能被加载这点和 qiankun 时代“必须导出生命周期”的要求完全不同。正因为接入足够轻我才有信心把整个流程做成一个 Vue CLI 插件否则光是适配各种项目形态就够写半年维护文档了。2. Vue CLI 插件到底帮我们组装了什么2.1 Vue CLI 插件的两个入口generator 和 service plugin我们常说的 Vue CLI 插件其实包含两部分能力。第一部分叫 generator作用是在vue create或vue invoke的时候往项目里写入文件、改package.json、改入口文件常用 API 有extendPackage、render、injectImports。第二部分叫 service plugin它是在项目启动vue-cli-service serve/build时执行的可以注册 webpack 插件、修改 webpack 配置。很多只写过业务代码的同事第一次接触这个机制时容易懵觉得插件不就是把一段代码复制到项目里吗其实不是它更像一个“项目生成器”可以根据用户输入动态地生成不同形态的代码。这套源码里的vue-cli-plugin-microapp就同时做了这两件事。generator 负责把 micro-app 依赖写入package.json往main.js里注入microApp.start()再根据用户选择生成主应用或子应用需要的vue.config.js和路由示例文件。service plugin 的作用更偏辅助比如根据--type child这样的参数把开发服务器的跨域头自动加上或者把publicPath调成 micro-app 子应用更友好的值。2.2 源码文件里主应用和子应用模板怎么分我把这套源码解压后目录大致是这样vue-cli-plugin-microapp/ ├── generator/ │ ├── index.js │ └── template/ │ ├── main/ │ │ ├── src/App.vue │ │ ├── src/main.js │ │ └── vue.config.js │ └── child/ │ ├── src/main.js │ ├── src/router/index.js │ └── vue.config.js ├── index.js └── package.jsongenerator 里会根据用户在命令行传给插件的type字段决定渲染template/main还是template/child。如果你把它们两个都生成就是一个完整的微前端演示工程如果团队里已有主应用可以只生成 child 模板。这个分离思路很好它没有把主应用和子应用混在一个模板里而是让模板按角色区分这样在团队里批量初始化新子应用时不会把主应用的东西一并带出来。2.3 用插件参数控制生成目标在 Vue CLI 的invoke阶段插件可以通过api.prompt和命令行选项让用户填参数。我在这套源码里看到的设计是支持--type main、--type child或者--type all。比如vue create my-app # 在 my-app 里手动调用本插件并选择主应用模式 vue invoke vue-cli-plugin-microapp --type main如果生成的是子应用它还会额外往src/router/index.js里写上base: window.__MICRO_APP_BASE_ROUTE__ || process.env.BASE_URL这段代码。这意味着在插件层面已经把最容易写错的一行路由 base 给处理掉了。这个细节对实际项目帮助很大因为我见过太多次忘记改base导致子应用路由跳转总是带根路径的问题。用插件生成之后这类低级错误基本就绝迹了。3. 从源码中拆出一套可抄的主应用配置3.1 主应用启动 micro-app 的最短路径主应用里打开一个子应用最短路径只需要三步。第一步安装micro-zeta/micro-app第二步在main.js里调用microApp.start()第三步在任意 Vue 组件里写micro-app标签。源码里的main.js大概是import Vue from vue import App from ./App.vue import router from ./router import microApp from micro-zeta/micro-app Vue.config.productionTip false microApp.start() new Vue({ router, render: (h) h(App), }).$mount(#app)需要注意microApp.start()必须在new Vue()之前调用否则组件里的micro-app标签可能无法被框架正确注册。这个顺序问题我在刚接的时候踩过一次后来养成了习惯把start()放在所有应用启动逻辑最前面。如果你在项目里还用了 ElementUI 之类的组件库也是一样的原则先启动 micro-app再做业务组件的Vue.use()可以避免一些莫名其妙的渲染顺序问题。3.2 vue.config.js 里真正不能省的三行配置主应用要加载的是一个指向本地开发服务器的子应用跨域问题是绕不开的。源码里主应用的vue.config.js保留了这三行关键配置module.exports { devServer: { port: 3000, headers: { Access-Control-Allow-Origin: *, }, }, }port是主应用的固定端口headers是为允许子应用资源被跨域访问。如果省掉headers子应用在主应用页面里大概率会因为跨域拿不到 JS 资源控制台直接报 CORS 错误。这里要提醒一下Access-Control-Allow-Origin: *只适合开发环境生产环境应该由 Nginx 或网关统一配置更严格的跨域策略否则你所有子应用的接口和数据都可能被任意站点读取这是一个很现实的安全漏洞。3.3 页面里如何把子应用挂上去主应用里最典型的用法是在菜单切换后把子应用挂到某个路由对应的组件里。模板里的App.vue或views/Child.vue大概长这样template div classlayout micro-app namechild-vue urlhttp://localhost:3001/ baseroute/child shadowDOM / /div /templatename是子应用的唯一标识不能和别的子应用重复url是子应用的开发服务器地址baseroute表示子应用挂在主应用的哪个路由前缀下这个值会和子应用路由的base对应shadowDOM是为了开启更严格的样式隔离。实际项目里如果一个页面有多个子应用它们的name必须不同否则后一个会把前一个覆盖掉。还有一种情况是同一个子应用被多个菜单引用这时候建议用name加菜单 id 的方式区分而不是写死同一个名字。4. 子应用侧我建议你保留的几个要点4.1 子应用其实可以不改代码但我的建议是改一行micro-app 官方宣传子应用可以零改造接入。这句话对了一半对于纯静态页面确实能在不修改子应用代码的情况下被主应用加载但一旦涉及路由跳转、接口请求、环境变量判断还是建议在子应用里留一个“感知微前端环境”的入口。最简单的一行代码就是判断全局变量window.__MICRO_APP_ENVIRONMENT__是否存在从而决定运行阶段要做哪些特殊处理。比如你的子应用里有个登录逻辑独立运行时需要走完整的登录页流程被 micro-app 接管时可能希望直接复用主应用的登录态。这时候你就可以在入口文件加一个判断if (window.__MICRO_APP_ENVIRONMENT__) { // 微前端环境直接走主应用带来的授权信息 } else { // 独立运行跳转到登录页 }这种代码虽然只多了一个条件分支但避免了后续在业务代码里到处判断环境把“环境感知”收敛在入口层其他业务组件不用关心自己到底是被谁挂载的。4.2 路由 base 用MICRO_APP_BASE_ROUTE兜底在子应用的 router 配置里我的建议是直接写成const router new VueRouter({ mode: history, base: window.__MICRO_APP_BASE_ROUTE__ || process.env.BASE_URL, routes, })这段代码的意思是当子应用被 micro-app 接管时base使用主应用传入的baseroute当子应用单独跑时用 webpack 默认的BASE_URL。这样两种模式下路由都不会乱。如果漏掉这个判断常见症状是主应用里点击侧边栏子应用总是跳到根路径/因为子应用内部不知道自己是寄生在/child这个前缀下的。在 Vue Router 3 的写法里这个base配置是命中要害的漏掉它基本等于子应用路由全废。4.3 端口、跨域和独立运行调试子应用在开发环境得固定一个端口比如3001并且devServer.headers也要配跨域。原因很简单micro-app 的加载方式是主应用直接请求子应用的资源如果子应用开发服务器不返回跨域头浏览器会直接拦截。为了让子应用既能独立运行也能被主应用包裹vue.config.js通常保留这样一段module.exports { publicPath: process.env.NODE_ENV production ? /child/ : /, devServer: { port: 3001, headers: { Access-Control-Allow-Origin: *, }, }, }publicPath的生产值需要按实际部署目录调整如果子应用部署在https://example.com/child/那这里就写/child/。写错了最直接的后果是子应用的 JS、CSS、图片全部 404。所以我在模板里专门把这个配置标成“部署前必改项”避免开发同学把它们直接推到生产环境。5. 跑通整个源码后最容易翻车的三个细节5.1 样式隔离body 背景和全局弹窗最容易穿帮micro-app 默认会做样式隔离但如果你在子应用的全局样式里写了body、html这种全局标签选择器隔离可能失效导致子应用的背景色或字体影响主应用。方案有几种要么在主应用和子应用里都约定不要直接改body而是用根组件上的 class要么在micro-app标签上开启shadowDOM。开启 shadowDOM 后子应用的 DOM 会被包进一个 shadow root外部样式基本不会渗进去但要注意弹层、Modal 如果被挂到document.body上可能会穿出 shadow root需要特殊处理。我在源码里的 child 模板看到一个很实用的习惯把全局背景色写在了根组件的样式里而不是写在style.css的body选择器里。这个小细节不起眼但能省掉不少样式排查时间。你想想当主应用和子应用都是深色主题时突然有一个子应用把body背景强制改成了白色整个页面就会像是一块白斑贴上去视觉上非常突兀。5.2 静态资源 404publicPath 和 baseurl 的三角关系我在源码里跑通第一个示例时遇到的第一个坑是子应用里的图片资源加载 404。原因很简单开发环境publicPath: /还能用一旦主应用通过/child前缀访问子应用子应用内部相对路径的资源就全部从/去找但根路径属于主应用自然 404。所以子应用生产环境的publicPath要和部署目录保持同层比如部署在/child/下就写成/child/。如果是挂在网关不同域名下也要按实际域名来配。这个点没有统一答案需要根据你们公司的基础设施决定但一定不能默认/。我见过一个团队上线后所有子应用图标都裂了查了半天发现就是 publicPath 忘了改。为了避免这类问题源码里在 plugin 的 service plugin 部分会根据options.type自动给子应用注入一段环境判断把 publicPath 设成window.__MICRO_APP_BASE_ROUTE__ /或直接使用process.env.BASE_URL这样可以同时兼容独立部署和微前端部署两种模式。5.3 子应用热更新失效时先查 devServer 配置开发环境下子应用被主应用包裹后再开热更新经常出现“主应用里改了子应用代码页面不刷新”的情况。这通常不是 micro-app 的问题而是子应用 devServer 的host、port、headers没配对或者 Vue CLI 默认的代理public配置没有指向可访问地址。我的排查方法是把子应用单独在浏览器里打开一次确认热更新功能正常再切回主应用里看。如果单独模式正常包裹模式失效那就检查子应用 webpack 的output.globalObject和devServer.allowedHosts配置让服务端允许来自主应用域名的跨域请求。还有一个隐藏点是子应用的 devServer 默认绑定的 host 可能是localhost。主应用如果用127.0.0.1访问子应用地址却写localhost:3001浏览器会认为它们是不同的源导致 cookie 或缓存隔离问题。我建议在主应用的url里统一用localhost子应用 devServer 的 host 也写成localhost避免混用。下表是我在跑这套源码时遇到频率最高的几个问题可以直接对照排查问题现象大概率原因处理方案主应用页面白屏控制台 CORS 报错主应用或子应用 devServer 缺少跨域头给两侧 devServer.headers 加上Access-Control-Allow-Origin: *子应用路由跳转总是回根路径子应用 router 没配置base: __MICRO_APP_BASE_ROUTE__按上文 4.2 的写法处理子应用背景色覆盖到整个页面子应用全局样式直接写了body或html改用根组件 class或开启shadowDOM子应用图片、字体 404publicPath和实际部署目录不一致根据部署路径调整publicPath改了子应用代码不热更新devServer 跨域头、host 或 allowedHosts 配置问题先单独跑子应用验证再排查微前端下的跨域和 host 设置6. 从“能跑”到“好用”这个模板后续还能怎么扩展6.1 把插件发布到私有 npm让“一键生成”成为团队基建这套源码的价值不在跑通一个 demo而在于把它发布到你们公司的私有 npm 仓库。之后每个新项目只需要执行vue add company/vue-cli-plugin-microapp就能得到一个符合团队规范的主应用或子应用骨架。这一步能把微前端接入的隐性知识全部沉淀下来后来的人不需要再去翻 micro-app 文档和网上零散的踩坑帖。我在团队里落地这件事之后新同学入职第一天就能自己生成一个子应用并且跑起来接入成本比之前人工复制配置少了一大截。6.2 在模板里塞公共能力登录态、主题、埋点插件生成模板时建议把团队公共能力一并放进去。比如主应用的登录鉴权、菜单权限、国际化、主题变量子应用的全局埋点、上报逻辑、统一错误处理。这些能力如果不在模板里固化每个项目各自实现一遍最后风格差异会很大。在 Vue CLI 插件里可以用api.render渲染统一的公共组件和 utils 目录再用api.extendPackage统一引入依赖。这样生成出来的项目天然带上一套团队规范不需要再花大量 code review 去纠正风格。6.3 用这套模板时我的最终建议我的建议是不要把这套模板当成“万能钥匙”。micro-app 解决了大多数应用间隔离和通信问题但微前端的核心复杂度在业务上子应用之间的路由协议怎么定、公共依赖如何共享、权限如何透传、异常时如何降级。模板能把集成成本打下来真正决定这套架构能不能长期跑下去的是团队内部有没有统一的规范和执行。等你在两三个项目里跑顺了再回头看这个基于 Vue CLI 插件的 micro-app 项目会发现它最大的价值不是某个配置文件写得有多妙而是让整个团队以一种标准方式切入微前端。插件机制带来的最大好处是可持续演进今天的模板是三四个文件过半年团队规范化了可以再往模板里加组件、加工具函数、加部署脚本。只要模板还在更新微前端架构的维护成本就不会随着项目数量线性增长。本文还有配套的精品资源点击获取