2020年微前端踩坑回顾

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,趟一趟水。

实践

因为这次重构和其他需求是并行的,将任务拆解为以下几项

  1. 涉及到梳理业务模块及依赖关系,根据依赖关系抽取公共模块。
  2. 将必要模块放在root应用中,如登录,权限,导航等等。
  3. 如何几个子项目如何构建,部署,上线
  4. 项目结束后如何跟旧项目的feature 解决冲突
  5. 抽取公共模块后,原项目的引用路径会变化,如何使用脚本来做这件事。

遇到的问题

重构嘛,肯定会遇到问题。但是qiankun没有太多问题,花费我巨量时间的还是项目中的一些问题,举一两个小例子吧,没太多营养,也不想浪费笔墨。

  1. 将公共样式和公共JS逻辑抽取到公共模块后,使用了import-html-entry加载,但是css的加载顺序会变化。如此脆弱的css,顺序变一下最终渲染的就会不同。
  2. 公共模块中的postcss解析不对,postcss-nested这个插件,在webpack升级后需要升级,但是升级到最新Version 5会报错,还原到原来Version 1还是还会报错,试着往下降了一版到Version 4,可以work。难受。
  3. 脚手架从webpack使用vue-cli后icon,竟然无法展示,具体记录在记一次Icon没展示的bugfix
  4. store如何控制,因为是项目拆分,原来公用一个的vuex现在还想维持原状,不想做过多改动,最后使用registerModule将子应用的store注册为主应用的一个module,幸运的是大部分代码都是用getter来获取store中的值,getter的注册具有全局命名空间,无论真是的store是在全局还是子模块。
  5. 不想每次都启动N+1个项目,如何处理呢,临时使用npm-run-all来过渡。
  6. 权限如何控制? 其实本来权限也有后端设置的cookie来决定,但是原来是一个spa项目,404页面和* 页面,都需要处理。因为不属于该项目的路由,需要去适配其他的子应用,而不是展示一个404。
  7. 子应用之前切换路由,不能使用vue-router的 name,而需要用path。

待解决的问题

  1. 不想每次都启动N+1个项目,考虑实现一个类似 import-map-overrides 的工具。可以动态改变publicPath,只启动需要调试的子项目即可。
  2. 多个Vue子项目同时启动后,vue-devtool时不时的失效。说不清就加载哪一个项目的状态,我在使用single-spa时没有这个问题。有待深入调查
  3. 对于使用镜像来部署的场景,每个镜像中跑一个nginx,里边serve一个html感觉有点浪费,因为js,css,font,img等等全都放cdn上,对于一个三个子项目的项目,每个项目2个节点,有点浪费。

写在最后的话

回到最初的愿景,我们还没实现,因为拆分才是我们重构的第一步,下一步要进一步细分,将服务拆的更小,然后才好进行无论是Vue升级,还是UI库升级。

项目上线灰度结束,发现花时间最多的

  1. 是处理cookie鉴权
  2. 多个项目间路由的处理
  3. 并行项目间合并代码
  4. 多个项目构建太慢如何优化

为了分析组件间的依赖关系,通过数据说话来决定哪些utils和components需要抽取到公共模块(被依赖太多和被main.js所依赖都需要抽取),自己实现了nodejs脚本来分析webpack的stat文件。
通过自动合并代码及查看diff,又写了个nodejs脚本来自动合并。
也很有趣。

Reference

  1. 飞冰官网:https://ice.work/docs/icestark/about
  2. 飞冰作者分享:https://zhuanlan.zhihu.com/p/88449415
  3. qiankun官网:https://qiankun.umijs.org/zh
  4. qiankun作者博客:https://zhuanlan.zhihu.com/p/95085796
  5. qiankun作者博客:https://zhuanlan.zhihu.com/p/200775337
  6. https://juejin.im/post/6856569463950639117#heading-15
  7. signle-spa官网:https://single-spa.js.org/