2020年已经过去,在家办公了小半年,回顾过去,Time flies。
今年听说是微前端的大年,应该每个前端都会听说过微前端的概念,就像几年前angular,vue,react流行时那样。我个人是特别的佩服single-spa的作者John Denning。大概2016-2017年的样子,Angularjs出了巨大变更的Angular,React在国外也风光无两。同样是面对新旧框架不兼容的情况,我们的方案是重构,用Angular5重构项目,同时轮流负责修改之前Angularjs1.4的bug。而John写了第一版的single-spa来解决了这个问题,在新feature上使用React,已有页面维持旧项目来逐步过渡。
今年先后试用了single-spa和qiankun来对两个项目做了重构,使用single-spa来改造Mis的过往总结在这。这篇文字注意记录一下使用qiankun遇到的问题。这里的问题必须解释一下不是qiankun的问题,而是我们项目中case by case的问题。
项目背景
先介绍一下为啥要用qiankun来拆分滴滴云控制台,当时考虑我们的控制台开发体验差,热更新慢,太臃肿,无法引入新的技术栈、升级组件库,为了更好支持平台化建设项目。遂考虑微服务拆分项目。
这里来盗个kuitos知乎上的图,如果想做一个长长久久公有云控制台,考虑迭代和升级越早收益越高。
愿景
希望将控制台庖丁解牛,拆分成几个独立的项目。每一个可以独立部署,独立开发。自由选择和升级技术栈,UI库,热更新快速。
框架对比
框架 | github stars(2020.10.29) | 主应用 | 共享依赖 | 子应用可否单独开发调试 | 部署 | entry | js,css隔离 | 路由控制 | 应用间通信 | 工程化 | 官方支持 |
---|---|---|---|---|---|---|---|---|---|---|---|
飞冰 | 906 | 必须react | 无 | 可独立启动的子应用,可以 | 如飞冰的截图,子应用单独部署 | html,js,简单如iframe | 暂未完善 | 简单的路由规则 | 有方案 | 无 | 没有专门的群,效率差 |
qiankun | 7.3k | qiankun | 无 | 可独立启动的子应用,可以 | 子应用单独部署 | html,js,简单如iframe | js沙箱,css shadow dom等方案 | 基于single-spa | 没方案,但有一些建议 | 无 | 有群,效率一般 |
single-spa | 7.6k | single-spa | utility-modules-styleguide-api-etc | import-map-override完美解决 | 子应用部署,需更新import-map | 需要systemjs,有一定成本 | 官方未提供建议 | 精准的路由控制 | 有方案 | 有 | slack,效率一般。有热心John |
大概对比下来,single-spa和qiankun各有千秋。但是single-spa要求的是子项目单文件入口,如果html中插入了多个chunk,single-spa无法保证其按顺序加载,对我们的项目冲击较大,而qiankun是根据html解析,将其css script链接进行加载,无需改动,最后选择了qiankun,趟一趟水。
实践
因为这次重构和其他需求是并行的,将任务拆解为以下几项
- 涉及到梳理业务模块及依赖关系,根据依赖关系抽取公共模块。
- 将必要模块放在root应用中,如登录,权限,导航等等。
- 如何几个子项目如何构建,部署,上线
- 项目结束后如何跟旧项目的feature 解决冲突
- 抽取公共模块后,原项目的引用路径会变化,如何使用脚本来做这件事。
遇到的问题
重构嘛,肯定会遇到问题。但是qiankun没有太多问题,花费我巨量时间的还是项目中的一些问题,举一两个小例子吧,没太多营养,也不想浪费笔墨。
- 将公共样式和公共JS逻辑抽取到公共模块后,使用了import-html-entry加载,但是css的加载顺序会变化。如此脆弱的css,顺序变一下最终渲染的就会不同。
- 公共模块中的postcss解析不对,postcss-nested这个插件,在webpack升级后需要升级,但是升级到最新Version 5会报错,还原到原来Version 1还是还会报错,试着往下降了一版到Version 4,可以work。难受。
- 脚手架从webpack使用vue-cli后icon,竟然无法展示,具体记录在记一次Icon没展示的bugfix
- store如何控制,因为是项目拆分,原来公用一个的vuex现在还想维持原状,不想做过多改动,最后使用
registerModule
将子应用的store注册为主应用的一个module,幸运的是大部分代码都是用getter来获取store中的值,getter的注册具有全局命名空间,无论真是的store是在全局还是子模块。 - 不想每次都启动N+1个项目,如何处理呢,临时使用npm-run-all来过渡。
- 权限如何控制? 其实本来权限也有后端设置的cookie来决定,但是原来是一个spa项目,404页面和* 页面,都需要处理。因为不属于该项目的路由,需要去适配其他的子应用,而不是展示一个404。
- 子应用之前切换路由,不能使用vue-router的 name,而需要用path。
待解决的问题
- 不想每次都启动N+1个项目,考虑实现一个类似 import-map-overrides 的工具。可以动态改变publicPath,只启动需要调试的子项目即可。
- 多个Vue子项目同时启动后,vue-devtool时不时的失效。说不清就加载哪一个项目的状态,我在使用single-spa时没有这个问题。有待深入调查
- 对于使用镜像来部署的场景,每个镜像中跑一个nginx,里边serve一个html感觉有点浪费,因为js,css,font,img等等全都放cdn上,对于一个三个子项目的项目,每个项目2个节点,有点浪费。
写在最后的话
回到最初的愿景,我们还没实现,因为拆分才是我们重构的第一步,下一步要进一步细分,将服务拆的更小,然后才好进行无论是Vue升级,还是UI库升级。
项目上线灰度结束,发现花时间最多的
- 是处理cookie鉴权
- 多个项目间路由的处理
- 并行项目间合并代码
- 多个项目构建太慢如何优化
为了分析组件间的依赖关系,通过数据说话来决定哪些utils和components需要抽取到公共模块(被依赖太多和被main.js所依赖都需要抽取),自己实现了nodejs脚本来分析webpack的stat文件。
通过自动合并代码及查看diff,又写了个nodejs脚本来自动合并。
也很有趣。
Reference
- 飞冰官网:https://ice.work/docs/icestark/about
- 飞冰作者分享:https://zhuanlan.zhihu.com/p/88449415
- qiankun官网:https://qiankun.umijs.org/zh
- qiankun作者博客:https://zhuanlan.zhihu.com/p/95085796
- qiankun作者博客:https://zhuanlan.zhihu.com/p/200775337
- https://juejin.im/post/6856569463950639117#heading-15
- signle-spa官网:https://single-spa.js.org/