十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

微前端-qiankun

微前端-qiankun 微前端与 qiankun 学习笔记一、微前端概述微前端Micro Frontends是一种将前端应用拆分成多个独立、可部署的部分的架构模式每个部分可以由不同的团队、技术独立开发、测试、部署和维护。这种架构类似于后端的微服务是为了应对前端项目复杂难维护、技术栈多种多样、多团队协同冲突等问题提出的。二、核心思想微前端的核心思想是将大型单体前端应用拆分为多个独立、自治、可独立开发部署的小型子应用微应用再通过主应用基座统一整合最终对用户呈现为一个完整、无感知的整体。三、基本要素技术栈无关主应用不强制限制子应用技术栈Vue、React、Angular、jQuery 等应用可以在同一基座中共存。独立开发部署子应用可以拥有独立仓库、独立构建流程和独立发布节奏。运行时集成主应用负责路由分发、资源加载、生命周期调度和公共能力注入。应用隔离子应用之间应尽量隔离样式、全局变量、事件监听、定时器和全局状态避免互相污染。通信可控主应用与子应用之间通过 props、事件总线、状态管理或约定 API 通信避免随意读写全局对象。四、qiankun 概述在中大型后台管理系统迭代中老项目技术栈老旧、新业务快速迭代、多团队并行开发是常态qiankun 作为蚂蚁开源微前端方案凭借 JS/CSS 沙箱、多技术栈兼容、独立部署特性成为行业首选。qiankun是一个基于single-spa封装的微前端框架用于帮助开发者构建生产可用的微前端架构。它可以把多个独立开发、独立部署的前端应用聚合到同一个主应用中并通过路由规则控制子应用的加载、挂载和卸载。注意qiankun 子应用需要支持独立运行。如果子应用依赖主应用传入的 props、全局状态、用户信息或鉴权信息需要提供默认值、降级逻辑或 mock 数据避免独立启动时报错。五、qiankun 的核心设计理念简单接入qiankun 提供registerMicroApps、start、loadMicroApp等 API主应用只需要配置子应用名称、入口地址、挂载容器和激活规则就可以完成基本接入。技术栈无关子应用可以使用 Vue、React、Angular、jQuery 等不同技术栈只要按 qiankun 约定导出生命周期即可接入。独立开发部署子应用可以独立开发、独立构建、独立发布主应用通过入口地址加载子应用资源。运行时隔离qiankun 通过 JS 沙箱、样式隔离和生命周期管理降低子应用之间互相污染的风险。六、qiankun 的特性基于 single-spa在 single-spa 的生命周期调度能力之上提供更易用的微前端接入方式。HTML Entry主应用可以通过子应用入口 HTML 自动解析和加载 JS、CSS 等资源降低接入成本。JS 沙箱隔离子应用对全局对象的修改降低全局变量污染风险。样式隔离通过样式作用域或 Shadow DOM 等方式减少 CSS 冲突具体能力与 qiankun 版本和配置有关。资源预加载可在浏览器空闲时预加载子应用资源提高后续访问速度。独立部署子应用可以独立构建和发布主应用通过入口地址加载最新子应用。七、qiankun 快速上手一个最小 qiankun 项目通常包含qiankun-demo/ ├── main-base/ # 主应用也叫基座应用 ├── vue2-sub/ # Vue2 子应用 └── vue3-sub/ # Vue3 子应用基本流程主应用安装 qiankun。主应用注册子应用入口、挂载容器和激活规则。子应用开启跨域访问。子应用导出 bootstrap、mount、unmount 生命周期。子应用配置打包格式和资源 publicPath。分别启动主应用和子应用通过主应用路由访问子应用。八、主应用配置基座项目8.1 安装 qiankun 依赖cdmain-basenpminstallqiankun^2--save8.2 注册子应用src/main.js)import{createApp}fromvueimportAppfrom./App.vueimport{registerMicroApps,start}fromqiankun// 注册两个子应用registerMicroApps([{name:vue2-sub,// 子应用唯一标识entry:http://localhost:8081,// 本地开发地址container:#micro-container,// 挂载DOM容器IDactiveRule:/app-vue2,// 路由匹配规则访问该路由加载子应用},{name:vue3-sub,entry:http://localhost:8082,container:#micro-container,activeRule:/app-vue3,}])// 启动qiankunstart()createApp(App).mount(#app)8.3 增加子应用挂载容器主应用注册代码// src/main.jsimport{createApp}fromvue;importAppfrom./App.vue;importrouterfrom./router;import{registerMicroApps,start}fromqiankun;registerMicroApps([{name:vue2-sub,entry:http://localhost:8081,container:#micro-container,activeRule:/app-vue2,props:{token:mock-token,from:main-app}},{name:vue3-sub,entry:http://localhost:8082,container:#micro-container,activeRule:/app-vue3,props:{token:mock-token,from:main-app}}],{beforeLoad:app{console.log([qiankun] before load:,app.name);returnPromise.resolve();},beforeMount:app{console.log([qiankun] before mount:,app.name);returnPromise.resolve();},afterUnmount:app{console.log([qiankun] after unmount:,app.name);returnPromise.resolve();}});start({prefetch:true,sandbox:{experimentalStyleIsolation:true}});constappcreateApp(App);app.use(router);app.mount(#app);App.vue挂载容器template div classmain-wrap !-- 主应用菜单跳转 -- router-link to/app-vue2进入 Vue2 子应用/router-link | router-link to/app-vue3进入 Vue3 子应用/router-link !-- 子应用挂载节点 -- div idmicro-container/div router-view / /div /template九、子应用改造接入 qiankun 的子应用通常需要完成以下改造开启跨域访问确保主应用可以加载子应用 HTML、JS、CSS 等资源。导出bootstrap、mount、unmount生命周期。在独立运行时自行渲染在 qiankun 环境中等待主应用调用mount。设置运行时 publicPath避免异步 chunk、图片、字体等资源路径错误。如果子应用使用 history 路由需要让子应用路由 base 与主应用activeRule保持一致。Webpack 子应用需要配置 UMD 输出格式。Vite 子应用需要使用对应的 qiankun 适配方案。在unmount中销毁应用实例、事件监听、定时器、订阅和全局副作用。Vue2 Webpack 子应用接入代码Vue2 子应用入口import./public-path;importVuefromvue;importAppfrom./App.vue;importrouterfrom./router;Vue.config.productionTipfalse;letinstancenull;functionrender(props{}){const{container}props;constmountNodecontainer?container.querySelector(#app):document.querySelector(#app);if(!mountNode){thrownewError([vue2-sub] mount node #app not found);}instancenewVue({router,render:hh(App)}).$mount(mountNode);}if(!window.__POWERED_BY_QIANKUN__){render();}exportasyncfunctionbootstrap(){console.log([vue2-sub] bootstrap);}exportasyncfunctionmount(props){console.log([vue2-sub] mount,props);render(props);}exportasyncfunctionunmount(){console.log([vue2-sub] unmount);if(instance){instance.$destroy();if(instance.$el){instance.$el.innerHTML;}instancenull;}}Vue2 子应用public/index.htmldividapp/divVue CLI / Webpack 子应用通常需要配置 UMD 输出。constpackageNamerequire(./package.json).name;module.exports{devServer:{port:8081,headers:{Access-Control-Allow-Origin:*}},configureWebpack:{output:{library:${packageName}-[name],libraryTarget:umd,chunkLoadingGlobal:webpackJsonp_${packageName}}}};注意jsonpFunction多见于 Webpack 4chunkLoadingGlobal多见于 Webpack 5。子应用被主应用加载时异步 chunk、图片、字体等资源路径容易错所以要配置运行时 publicPath。Vue CLI 子应用可以新增// src/public-path.jsif(window.__POWERED_BY_QIANKUN__){// eslint-disable-next-line no-undef__webpack_public_path__window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;}然后在入口文件最顶部引入注意必须放在其他业务 import 之前import./public-path;路由 base// RouterimportVuefromvue;importVueRouterfromvue-router;Vue.use(VueRouter);constrouternewVueRouter({mode:history,base:window.__POWERED_BY_QIANKUN__?/app-vue2:/,routes:[{path:/,component:()import(./views/Home.vue)}]});exportdefaultrouter;Vue3 子应用接入代码Vue3 子应用入口import{createApp}fromvue;importAppfrom./App.vue;importrouterfrom./router;letappnull;letmountNodenull;functionrender(props{}){const{container}props;mountNodecontainer?container.querySelector(#app):document.querySelector(#app);if(!mountNode){thrownewError([vue3-sub] mount node #app not found);}appcreateApp(App);app.use(router);app.mount(mountNode);}if(!window.__POWERED_BY_QIANKUN__){render();}exportasyncfunctionbootstrap(){console.log([vue3-sub] bootstrap);}exportasyncfunctionmount(props){console.log([vue3-sub] mount,props);render(props);}exportasyncfunctionunmount(){console.log([vue3-sub] unmount);if(app){app.unmount();appnull;}if(mountNode){mountNode.innerHTML;mountNodenull;}}如果项目使用 Vue CLI 4 / Webpack 4使用jsonpFunction如果项目使用 Webpack 5使用chunkLoadingGlobal。示例// vite.config.jsimport{defineConfig}fromvite;importvuefromvitejs/plugin-vue;importqiankunfromvite-plugin-qiankun;exportdefaultdefineConfig({plugins:[vue(),qiankun(vue3-sub,{useDevMode:true})],server:{port:8082,cors:true}});入口文件示例import{createApp}fromvue;import{renderWithQiankun,qiankunWindow}fromvite-plugin-qiankun/dist/helper;importAppfrom./App.vue;letappnull;functionrender(props{}){const{container}props;appcreateApp(App);app.mount(container?container.querySelector(#app):#app);}renderWithQiankun({bootstrap(){console.log([vite-sub] bootstrap);},mount(props){console.log([vite-sub] mount,props);render(props);},unmount(){console.log([vite-sub] unmount);app.unmount();appnull;}});if(!qiankunWindow.__POWERED_BY_QIANKUN__){render();}路由 base// Routerimport{createRouter,createWebHistory}fromvue-router;constroutercreateRouter({history:createWebHistory(window.__POWERED_BY_QIANKUN__?/app-vue3:/),routes:[{path:/,component:()import(./views/Home.vue)}]});exportdefaultrouter;十、常见问题1.__POWERED_BY_QIANKUN__是什么为什么有时是 undefinedwindow.__POWERED_BY_QIANKUN__是 qiankun 加载子应用时注入的运行时标识用来判断当前子应用是否运行在 qiankun 主应用中。它为undefined通常有几种情况子应用是独立运行的此时没有被 qiankun 加载属于正常情况。子应用入口代码执行时机不正确判断发生在 qiankun 注入之前。TypeScript 项目没有声明该全局变量导致类型报错。子应用构建格式或生命周期导出错误导致 qiankun 没有正确识别子应用。Vite 或 Webpack 项目没有按对应接入方式配置。TypeScript 类型声明// src/types/qiankun.d.tsexport{};declareglobal{interfaceWindow{__POWERED_BY_QIANKUN__?:boolean;__INJECTED_PUBLIC_PATH_BY_QIANKUN__?:string;}}2. 子应用资源 404 怎么办常见原因是子应用被主应用加载后异步 chunk、图片、字体等资源仍然按照子应用独立运行时的相对路径加载导致路径错误。解决方案Webpack 子应用配置运行时 publicPath。Vite 子应用使用对应 qiankun 适配插件。静态资源尽量使用绝对路径或 CDN 地址。检查子应用部署路径是否和入口地址一致。Webpack 解决代码if(window.__POWERED_BY_QIANKUN__){// eslint-disable-next-line no-undef__webpack_public_path__window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;}3. 子应用样式污染怎么办样式污染通常来自全局选择器例如body、html、.button、.container等。解决方案包括开启 qiankun 样式隔离配置。子应用样式增加命名空间。避免在子应用中直接修改body、html的全局样式。UI 组件库主题样式要避免互相覆盖。主应用配置start({sandbox:{experimentalStyleIsolation:true}});子应用样式命名空间示例.vue2-sub-app { .button { color: #f00; } .page-container { padding: 16px; } }子应用根节点template div classvue2-sub-app router-view / /div /template4. 子应用切走后仍然有定时器或事件怎么办原因通常是unmount中没有清理全局副作用。子应用被卸载时除了销毁 Vue/React 实例还要清理事件监听、定时器、订阅、全局状态等。示例lettimernull;functionhandleResize(){console.log(resize);}exportasyncfunctionmount(props){window.addEventListener(resize,handleResize);timersetInterval((){console.log(running);},1000);render(props);}exportasyncfunctionunmount(){window.removeEventListener(resize,handleResize);if(timer){clearInterval(timer);timernull;}instance.$destroy();instance.$el.innerHTML;instancenull;}十一、 高频面试题1. 什么是微前端解决什么业务痛点微前端是把大型单体应用拆分成多个独立子应用由主应用统一调度解决多团队并行开发、技术栈异构Vue2、Vue3、React通用项目体量过大打包慢、迭代耦合、牵一发动全身老旧项目存量改造不用一次性全量重构。2. 微前端常见实现方案有什么区别iframe隔离最强天然隔离 JS、CSS 和运行环境缺点是路由同步、通信、性能、弹窗层级、用户体验都比较麻烦。Web Components基于浏览器原生组件模型可结合 Shadow DOM 做样式隔离缺点是工程生态、团队使用习惯和复杂应用集成成本需要评估。运行时加载方案例如 qiankun主应用在运行时加载子应用资源并通过生命周期、沙箱、样式隔离完成应用集成。模块联邦方案例如 Webpack Module Federation / EMP更偏向构建和运行时模块共享适合跨应用复用模块或组件。3. qiankun 核心原理是什么HTML Entry主应用拉取子应用入口 HTML解析其中的script、link、style等资源再由 qiankun 加载并执行子应用。生命周期调度qiankun 基于 single-spa 管理子应用的bootstrap、mount、unmount、update。JS 沙箱通过代理或快照机制隔离子应用对全局对象的修改降低全局变量污染风险。样式隔离通过样式作用域、动态样式处理或 Shadow DOM 等方式减少 CSS 冲突。应用通信主应用可以通过 props 或全局状态机制向子应用传递数据和方法。4. qiankun 四个生命周期各自作用bootstrap子应用初始化只在首次加载时执行一次适合做不会频繁变化的初始化工作。mount子应用被激活时执行用于创建应用实例并挂载 DOM。unmount子应用失活时执行用于销毁实例、解绑事件、清理定时器、取消订阅、清空 DOM。update主应用手动触发子应用更新时执行使用频率较低。
返回列表