微前端
2020.5.30 前端早早聊微前端专场
什么是微前端?解决什么样的问题
玉伯:微前端的前提,还是得有主题应用,然后才有微组件或微应用,解决的是可控体系下的前端协同开发问题。(含空间分离带来的协作和时间延续带来的升级维护。
这个概念来源于后端微服务,将微服务概念用于浏览器端,以使多个SPA应用接入到框架应用中。
要解决的问题是团队平台系统多且相互独立,系统体量大且页面多,开发效率低、接入成本高。
基于这样的问题,有没有其他的解决方案?
- 巨石应用:随着页面增多,开发效率和稳定性会无法满足要求。
- iframe: 有用户体验问题,如果体量不大还是很推荐
- 框架组件:不满足一个系统的心智,同时长期维护成本高。
- 微前端:在大型系统的业务场景下,具备体验和效率的平衡点。
大型系统为什么不考虑iframe?
- 加载延迟
- 用户子应用返回时直接回到Home页
- 说白了就是用户体验和UI显示不太好
微前端业务价值
业务价值有下面几点:
- 应用过多时,域名太多,不容易记
- UI风格不一致,不易维护
- 多应用操作断层,希望更连贯些。
工程价值为:
微前端原理
以icestark为例,看看微前端做了什么:
路由劫持Proxy解决的问题:
- 应用间跳转: pushState/popState/replaceState/hashChange
- 应用内跳转: history
微前端内核
大致可以分为一个主应用框架和微应用容器 主应用框架负责的功能有:
- 渲染
- 加载
- 路由
- 通信
微应用容器包括 JS bundle context 和沙箱
展开来看各自具体需要解决的问题:
当前社区方案
- Single-Spa ✅
- Qiankun ✅
- Luigi 基于ifame ❎
- Feature-Hub ❎
- Federation (适合单体拆分,后面会有介绍)
- icestark
Single Spa vs Qiankun:
Qiankun实际上相当于single-spa + sandbox + import-html-entry
如何集成?
简单模式
精细模式
提供 Content 渲染区域给子应用
- 协定菜单数据结构,给子应用 menuService
- 提供自定义 Menu、Header、Footer 的自定义接口
- 注入统一请求库,利用 LRU 策略缓存接口
单体拆分
动态加载模块可能遇到的问题:
微前端和 Webpack 如何更好的结合?
Tobias Koppers 给出了答复:Federation, 但是目前文档还没跟上。
建议:可以去试着玩,但不要在生产上用。
Federation 比起其他解决方案有什么优势?
Federation 可以对依赖库做版本控制、自动加载缺失的 vender,可以支持远程加载,解决了 npm link 在开发时的不便利。
实施过程中可能遇到的问题
部分问题及解决的方案:
路由调度问题
思考:前端为什么要有路由以及路由产生的背景? 笔记:为了正确的维护页面的状态,可以通过url变化/页面交互来反映页面的状态变化。
需求是无论是点击跳转还是导航跳转/返回页面都能正常显示。
不同方案会带来哪些问题?
由简到难分别是:
- 主应用完全不处理路由:
主应用不根据路由调度子应用,子应用内部状态变化按原样反映到主应用路由。
问题:路由冲突,无法初始化路由。
- 子应用和主应用共享路由
主应用根据路由调度子应用,子应用内部状态变化根据特定规则反映到主应用的路由。
新的问题:需要将约定侵入到子应用的逻辑
// 使⽤子应⽤名称来对共享资源切⽚
const APP_NAME = 'cow';
<Route path={`/${APP_NAME}/home`} component={Home} />
<Route path={`/${APP_NAME}/detail`} component={Detail} />
- 子应用维护隔离的路由
主应用根据路由调度子应用,子应用内部动态变化反映到各自沙箱,互不干扰,但不会反映到主应用的路由中
新问题:由于⽆法感知⼦应⽤路由的变化,
主应⽤不能体现路由逻辑。
- 子应用路由同步回主应用
主应⽤根据路由调度子应⽤,⼦应用内部的状态变化反映到各自的沙箱中,互不干扰,并且最终会反映到主应⽤用的路由中。
ifram e路由变化时对主应用 history 有什么影响?
关键词:Joint Session History 标准
全局的样式冲突
Shadow DOM | Css Scope
业务代码样式隔离: CSS Modules,类名+哈希
基础组件样式隔离: namespace,在样式名前加前缀,统一修改
JS污染
- tc39/proposal-realms 类似于一个沙箱环境,但由于浏览器未被实现,还是得自己去写。
- new Proxy 拦截
// 创建沙箱
const sandbox = new Proxy(Object.create(null), {
get(){},
set(){},
has(){},
// ...
})
// 执行代码
const execScript = `with (sandbox) {${script}\n}`;
const execCode = new Function('sandbox', execScript);
execCode(sandbox);
某些库多版本
Externals => DLL
发布问题
因为应用之间有依赖,发布有次序性问题,除了 js 和 css 之外,对 html 也需要有 hash 版本( index.[hash].html ),同时需要开发一个配置中心,在模块发布成功后通知,并且主应用最后发布。
webpack升级问题
4.x->5(beta):把 loader 和 plugin 升到最新,有问题去 github issue 看
沙盒设计
沙盒在微前端中起到什么作用
沙盒能做什么
老的iframe问题:
- 一个站点拆成N个frame
- 每个frame单独一个独立域名
- 独立上下线,独立运行时
- 数据共用困难:登录身份、站内信、跨模块通信
前端沙盒应该像浏览器 Docker,具有下面优势
怎么做?
1.参考单核、操作系统进程模拟进程切换策略,
- 通过对路由切换的封装,模拟单进程
- 通过对事件循环封装,模拟单核多进程
2.用 Context 切换模拟线程安全,新沙盒即将激活时,查找当前激活中的沙盒。
- 保存现场,存储context
- 恢复之前的context
3.Context 切换的笛卡尔积
- 比较并切换
- 沙盒数量 N 的笛卡尔平方
- 退回「初始」context
- 恢复之前 diff 的 context
qiankun分享
核心价值:「技术栈无关」
为什么需要技术栈无关?
多个项目ABC,可能会有 React@16+webpack@5 和 jQuery+Gulp这种冲突,那么要解决的问题就不言而喻。
App Entry 选择和组合时机
- 运行时还是构建时?
- 构建时:构建时容易做公共依赖提取等优化,缺点是主子应用构建方案、工具耦合且主子应用必须一起发布
- 运行时:子应用独立构建,运行时动态加载,优点是主子应用完全解藕且与技术栈无关,缺点是有运行时损耗 ✅
- 入口选择,HTML 还是 JS ?
- HTML , 它包含了应用所需的所有信息:网页结构和元信息、js 和 css 等资源,✅
- JS: 便于构建时优化,但容易产生耦合,打包产物体积太大。
应用的隔离?
主要是 JS 的隔离,需要两个 JS 沙箱。Global Env 外部全局环境和 Render Env 内部沙箱环境
两种方法:
- 快照
- 应用挂载/卸载时记录快照
- 应用切换时快照恢复环境
- 缺点:不能支持多个实例
- Proxy
- 代理沙箱内部操作,不影响 Global Env
- 将副作用局限在 Render Env 内部
解决完 JS 隔离,再看样式冲突
- 动态样式表,当 A 切换到 B 时,样式也切换,删 A 激活 B, 但是只能保持一个应用激活,多个应用时没办法保证。
- 工程化手段 ✅
- 通过前缀依赖约定,容易出纰漏
- 通过编译生成不冲突的选择器名,但只能在构建时使用,依赖预处理器和打包工具
- CSS-in-JS,CSS 和 JS 编码到一起,可以彻底隔离,但会有一些运行时的开销
- Shadow DOM,实际应用时还是有一些问题,需要去改造子应用。
- 样式隔离 RFC: runtime css transformer
解决完隔离问题,就要看通信问题了。
- 基于URL通信,比如:URL 为 /b/func-log?data=aaa, 即调用 b 应用的 log 方法打印 data 为 aaa,缺点是消息能力弱
- 发布/订阅模型。windows 自带事件总线 CustomEvent 来分发事件,主子应用订阅。缺点是全局命名冲突,缺乏管控
- 基于 props, 主应用向子应用传递 state 和事件,子应用 setGlobalState,缺点是主子应用耦合较强有新的 API 学习成本
- 再有就是全局变量、共用 Redux 来实现子应用之间的通信。
具体使用还是看实际应用场景。
上面问题小结下可以由下面图代替:
书籍推荐
《知行》:技术管理
《领域驱动设计》:领域如何划分,DDD
《守望者(Watchmen)》: 美漫
《金字塔原理》 排样自己说/想/做/表演的能力
《松本行弘的程序世界》 深入理解程序设计背后的故事
相关文章
- shadowDom隔离方案: github.com/Wildhoney/R…
- qiankun2.0: zhuanlan.zhihu.com/p/131022025
- MDN-shadowDOM原理图:developer.mozilla.org/zh-CN/docs/…
总结
这一下午主要了解了以下几点:
- 什么是微前端
- 产生背景
- 业务场景
- 遇到的问题及解决方案
- 社区方案
再说一些与发展相关的想法,我之前在面试的时候别人问到项目亮点总是答不上来,还是因为遇到不会的问题总是直接上手找库、找框架,把重点放在了实现功能,而不去考虑这些框架的设计背景,他们的理念、原理,核心。(顶多是被动的去学习 Vue 源码)所以感觉这次的分享学到了很多。尤其是最后一场分享可以说是总结全文了。
前端早早聊大会目标成为用得上、听得懂、抄得走的技术大会,计划 2020 年办 >= 15 期,由前端早早聊与掘金联合举办,前端早早聊大会行程动态、录播视频/PPT/讲稿资料下载请关注 「前端早早聊」 公众号跟进。
6 月 13 日举办第九届 - 前端如何搞文档,报名请戳:www.huodongxing.com/go/tl9 ,海报及讲师行程如下:
6 月 20 日举办第十届 - 前端如何搞跨端跨栈,报名请戳:www.huodongxing.com/go/tl10 ,海报及讲师行程如下:
6 月 27 日举办第十一届 - 女生前端职业发展专场,报名请戳:www.huodongxing.com/go/tl11 ,海报及讲师行程如下:
看完若有启发,举手给作者点个赞吧