微前端 写在前面 中台部分业务模块多,单个仓库存放代码量多,阅读困难,难以维护,所以按照业务区分来拆分微前端,方便维护。本文介绍微应用的基本应用,并在最后介绍基本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