微前端 写在前面 中台部分业务模块多,单个仓库存放代码量多,阅读困难,难以维护,所以按照业务区分来拆分微前端,方便维护。本文介绍微应用的基本应用,并在最后介绍基本api。
links 
什么是微前端 微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。
核心价值 
技术栈无关
独立开发、独立部署
微应用仓库独立,前后端可以独立开发,部署完成后主框架自动完成同步更新 
 
增量升级
在面对各种复杂场景时候,很难对一个已经存在的系统进行全量的升级或者重构,而微前端是一种非常好的实施渐进式重构的手段和策略 
 
独立运行时
 
主应用 在对应的微应用仓库运行项目,在主应用注册相关微应用配置即可。
主应用不限制技术展 只需提供一个容器DOM,然后start即可
安装qiankun
$ yarn add qiankun # 或者 npm i qiankun -S
1、主应用 配置文件添加微应用注册配置 注册微应用并start
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import  { registerMicroApps, start } from  'qiankun' const  apps = [{ name : 'subapp' ,entry : '//localhost:3000/' ,container : '#subapp-container' ,activeRule : '/subapp-admin' ,}, { name : 'subapp_admin' , entry : '//localhost:4000/' ,container : '#subapp-container' ,activeRule : '#/subapp-admin-account' ,}, ] registerMicroApps (apps)start ()
这里我们是拆分中台应用,所以使用的语言都为vue,所以向微应用传递store、router、BaseRequest(axios请求封装),并且使用prefetchApps手动预加载指定的微应用静态资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import  { registerMicroApps, prefetchApps } from  'qiankun' import  store from  '@/store' import  router from  '@/router' import  BaseRequest  from  '@/utils/http' const  apps = [{ name : 'subapp' ,entry : '//localhost:3000/' ,container : '#subapp-container' ,activeRule : '/subapp-admin' ,props : {    store,     router,     BaseRequest ,     }, }, { name : 'subapp_admin' , entry : '//localhost:4000/' ,container : '#subapp-container' ,activeRule : '#/subapp-admin-account' ,props : {    store,     router,     BaseRequest ,     }, }, ] registerMicroApps (apps)prefetchApps (apps)
2、微应用 项目配置 微应用分为有 webpack 构建和无 webpack 构建项目,有 webpack 的微应用(主要是指 Vue、React、Angular)
这里只介绍有webpack构建且用vue框架时如何应用
1.新增 public-path.js 文件,用于修改运行时的 publicPath。
注意:运行时的 publicPath 和构建时的 publicPath 是不同的,两者不能等价替代。
 
2.微应用建议使用history模式的路由,需要设置路由base,值和它的activeRule是一样
3.在入口文件最顶部引入public-path.js,修改并导出三个生命周期函数
4.修改webpack打包,允许开发环境跨域和umd打包
vue微应用 1.在src目录新增 public-path.js 文件
1 2 3 4 if  (window .__POWERED_BY_QIANKUN__ ) {__webpack_public_path__ = window .__INJECTED_PUBLIC_PATH_BY_QIANKUN__  } 
2.这里在vue中使用hash路由
3.在入口文件最顶部引入public-path.js,修改并导出三个生命周期函数
以Vue3.x的入口文件为例:src/main.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 import  Vue , { createApp } from  'vue' import  { createRouter, createWebHashHistory, Router  } from  'vue-router' import  App  from  './App.vue' import  store from  './store/index' import  routes from  './router/index' import  './public-path.ts' let  instance : Vue .App <Element >let  router : Router const  routerBase = '/subapp-account' const  __qiankun__ = window .__POWERED_BY_QIANKUN__ interface IRenderProps  {     container : Element  | string     store?: any     [propname : string]: any } function  render (props: IRenderProps ) {    const  { container, store : mainStore } = props     window .$mainStore  = mainStore          router = createRouter ({         history : createWebHashHistory (__qiankun__ ? `${routerBase} /`  : '/' ),         routes     })     instance = createApp (App )     instance         .use (router)         .use (store)         .mount (             container instanceof  Element                  ? (container.querySelector ('#app' ) as  Element )                 : (container as  string)         ) } function  setupStore (props: IRenderProps ) {    props.onGlobalStateChange (         (value: any, prev: any ) =>              console .log (`[onGlobalStateChange - ${props.name} ]:` , value, prev),         true      )     props.setGlobalState  &&         props.setGlobalState ({             ignore : props.name ,             user : {                 name : props.name              },             routes         }) } if  (!__qiankun__) {    render ({ container : '#app'  }) } export  async  function  bootstrap (    console .log ('subapp bootstraped' ) } export  async  function  mount (props: any ) {    console .log ('mount subapp' )     setupStore (props)     render (props) } export  async  function  unmount (    console .log ('unmount college app' )     instance.unmount () } 
4.修改webpack打包,允许开发环境跨域和umd打包
除了代码中暴露出相应的生命周期钩子之外,为了让主应用能正确识别微应用暴露出来的一些信息,微应用的打包工具需要增加如下配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const  packageName = require ('./package.json' ).name ;module .exports  = {devServer : {    headers : {       'Access-Control-Allow-Origin' : '*' ,     }, }, output : {library : `${packageName} -[name]` ,libraryTarget : 'umd' ,jsonpFunction : `webpackJsonp_${packageName} ` ,}, }; 
配置gitlab CI
// 参考subapp-admin gitlab-ci.yml
微应用单独编译打包
这里只做简单解释 具体需要先了解 gitlab ci/cd 极狐GitLab CI/CD 入门 | 极狐GitLab 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 stages:   -  build-211-dev    -  build-211-sit    -  build-211-uat    -  build-211-pro  variables:    project:  admin    targetpage:  admin-sg     proRep:  admin-pro   build-211-dev:   stage:  build-211-dev    only:       refs:        -  /^dev$/       variables:        -  $CI_COMMIT_MESSAGE  =~  /\-\-build/     script:       -  sh  /home/gitlab/front-shell/projects/youxin-admin/admin.sh  -b  dev  -d  subapp-account  -r  admin-sg/subapp-account-sg    tags:       -  web-dev  
常见api registerMicroApps(apps, lifeCycles?) 
prefetchApps(apps, importEntryOpts?) 
常见问题 1、Sass变量全局配置问题 场景:新项目安装node-sass、和sass-loder后,自动导入样式文件还是无法读取全局变量。
解决方案:还需安装sass-resources-loader 包
疑问:其他项目同样版本并无安装以上依赖照样可以运行。估计是Webpack版本兼容问题。
1 2 3 4 5 6 7 8  css : {         loaderOptions : {             sass : {                 prependData : `@import "@/assets/styles/theme.scss";` ,             },         },     }, 
2、Vue路由切换卡顿问题 解决方案:watch route.name  而不是整个route响应式对象
[Deprecation] ‘window.webkitStorageInfo’ is deprecated. Please use ‘navigator.webkitTemporaryStorage’ or ‘navigator.webkitPersistentStorage’ instead. · Issue #3112 · vuejs/vue-next 
3、样式隔离问题 解决方案:添加命名空间
子服务使用element-ui: Failed to execute ‘getComputedStyle’ on ‘Window’: parameter 1 is not of type ‘Element’. · Issue #634 · umijs/qiankun 
4、打包缓存问题 解决方案:强制刷新,打包添加md5命名
Uncaught TypeError: Cannot read property ‘call’ of undefined · Issue #959 · webpack/webpack 
5、KeepAlive方案 解决方案:子应用使用KeepAlive,主应用控制需要缓存的组件列表
其他方案参考 
[Feature Request] 主应用多页签切换不同子应用的页面状态保持 · Issue #361 · umijs/qiankun 
6、Hash路由模式不触发unmont问题 解决方案:不同子项目才会触发,同子项目路由变更不触发
[Bug]似乎不支持hash路由 · Issue #118 · umijs/qiankun 
7、子路由前缀相同时,路由切换报错 报错提示: https://single-spa.js.org/error/?code=31&arg=mount&arg=application&arg=subapp-market&arg=3000 
解决方案:精确匹配路由规则即可
子应用路由前缀相同时,路由切换错误 #607