基于 iframe 的微前端框架 —— 擎天
vivo 互联网前端团队- Jiang Zuohan
一、背景
VAPD是一款专为团队协作办公场景设计的项目管理工具,实践敏捷开发与持续交付,以「项目」为核心,融合需求、任务、缺陷等应用,使用敏捷迭代、小步快跑的方式进行开发及质量跟踪,简化工作流程,帮助团队快速迭代并高效完成产品开发交付。
但早期VAPD以“一切皆可配置”的设计理念开发运行了两年,整个前端代码复杂混乱,组件庞大(需要支持多种配置),状态混乱,前端代码打包出来有50M之巨。这个项目难以为继,bug多、维护困难、新增功能处处受限,总之产品不满意、测试不满意、用户不满意。
因此改版是必然的选择,而改版的要求就是不能耽误用户继续使用,必须保证网站可用、逐步更新,因此微前端是必然的选择。
VAPD改版思路就是:
使用微前端框架,未改版部分作为子应用存在,继续为用户服务;
将项目模块制定系统应用,并逐个改版,降低项目复杂度;
逐步舍弃旧项目代码,将功能转移到新项目中,提升项目整体性能,提高代码可维护性。
二、什么是微前端
Techniques, strategies and recipes for building a modern web app with multiple teams that can ship features independently. —— Micro Frontends.
微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。
微前端将微服务的理念应用于浏览器端,即将单页面前端应用由单一的单体应用转变为把多个小型前端应用聚合为一的应用,各个前端应用独立开发、独立部署。
微前端架构旨在解决单体应用在一个相对长的时间跨度下,由于参与的人员、团队的增多、变迁,从一个普通应用演变成一个巨石应用(Frontend Monolith)后,随之而来的应用不可维护的问题。这类问题在企业级 Web 应用中尤其常见。
现行最出名的微前端是阿里的qiankun,qiankun 是一个基于 single-spa 的微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。
但我尝试试用qiankun后发现qiankun 的npm包常常滞后于qiankun源码,有些issue解决了但还要等待其发版;首次加载子应用页面出现抖动;子应用更新后报 ChunkLoadError 问题 。
同时它存在微前端框架常有的性能问题及冲突问题:
1、加载慢
基本上所有的微前端框架都需要先加载父框架,再加载子应用,都要经历如下图的流程。整个流程是串行的,相同流程需要走两遍,也就比普通的非微前端框架要慢1倍左右,直接影响了用户体验。
2、切换慢,每次切换都要重新走一次流程
微前端框架中不同子应用切换,需要销毁当前子应用,然后加载其他子应用。子应用又需要进行“下载html --> 下载javascript文件 --> 运行javascript代码 --> 请求服务端数据 --> 页面展示"整个流程,导致微前端框架切换应用卡顿不流畅。
3、容易出现样式冲突,不同子应用容易影响
传统微前端框架中所有子应用都在同一上下文中,如出现全局样式、全局监听、全局变量,便会对其他子应用有影响,容易出现样式冲突,内存泄漏,或者不可预知的bug。
三、Why Not Iframe
iframe 最大的特性就是提供了浏览器原生的硬隔离方案,不论是样式隔离、JS隔离这类问题统统都能被完美解决。
那为啥不使用iframe呢?
qiankun 团队也给出了原因:看这里 Why Not Iframe。
总结起来就是:
url 不同步。浏览器刷新 iframe url 状态丢失、后退前进按钮无法使用。
UI 不同步,DOM 结构不共享。想象一下屏幕右下角 1/4 的 iframe 里来一个带遮罩层的弹框,同时我们要求这个弹框要浏览器居中显示,还要浏览器 resize 时自动居中..
全局上下文完全隔离,内存变量不共享。iframe 内外系统的通信、数据同步等需求,主应用的 cookie 要透传到根域名都不同的子应用中实现免登效果。
慢。每次子应用进入都是一次浏览器上下文重建、资源重新加载的过程。
其中有的问题比较好解决(问题1),有的问题我们可以睁一只眼闭一只眼(问题4),但有的问题我们则很难解决(问题3)甚至无法解决(问题2),而这些无法解决的问题恰恰又会给产品带来非常严重的体验问题, 最终导致我们舍弃了 iframe 方案。
四、擎天框架设计
4.1 设计理论
基于以上微前端缺陷,设计新型的微前端框架需要满足以下要求:
快 —— 页面加载速度、应用切换速度是前端极致的追求,用户操作的响应速度永远是第一体验。
完全隔离 —— 不同子应用完全隔离,只在子应用项目初始化时设置一次,之后不需要在后续开发中及后期维护中考虑,降低开发的心智负担。
4.2 架构设计
擎天框架架构分为三层,分别是用户入口(浏览器地址栏),主应用层以及子应用集合层。
(1)浏览器(地址栏)是用户入口,用户通过输入URL进入该系统,同时浏览器地址栏URL根据应用页面进行变化,方便用户复制分享二次进入。
(2)主应用是擎天框架的关键部分,主要是由以下两部分组成:
路由引擎:实现浏览器地址栏与子应用展示隐藏的协调控制,用来控制用户第一次进入展示某个应用,当用户切换页面时需同步浏览器地址栏(方便用户复制,再次进入同一页面),并根据页面URL展示隐藏子应用。
数据共享引擎:实现子应用间的数据共享,保证各个应用间数据统一,如登录信息,用户信息等。用户在某个应用修改共享数据后,会同步到数据共享引擎,再分发给其他应用,从而保证共享数据一致。
(3)子应用集合层
该层为系统提前设置好的子应用集合,子应用由全屏iframe加载,同一时刻仅有一个子应用处于可视状态,其他子应用隐藏。当需要应用切换时,隐藏当前应用,显示需要展示的应用,瞬间切换。
子应用响应擎天的路由引擎及数据共享引擎,做到实时的路由同步与数据同步,保证整个微前端项目路由及数据统一。
五、擎天框架实现
擎天框架突破了 iframe UI不同步、URL不同步、数据不共享以及加载慢等问题,并将iframe作为页面容器存在,在实现硬隔离的同时做到了子应用瞬间切换,解决了微前端框架一直以来的通病,从而实现单应用级别的操作体验。
5.1全屏iframe、共同组件
解决问题:UI 不同步
全屏iframe是指整个浏览器窗口全部给子应用iframe,子应用接管浏览器所有功能,无论是全局拖动、全部蒙层、resizie等,由此UI不同步的问题便不存在了。
但不同应用有个相同的公用部分,因此需要把公共部分做成统一组件,发到npm包中,在每一个应用中引入就行。
5.2 父子应用异步加载
解决问题:加载慢
在前端静态页面中预留子应用dom节点,如下图 所示,每一个div有唯一id,代表一个预制子应用。同时该id对应子应用路由pathname,如New对应/New/*路由,即以New开头的路由全部对应id为"New"的子应用。
当用户进入页面后,父框架拿到浏览器url,并获取到pathname,从而知道需要首先加载那个子应用。并且直接创建iframe,并直接挂到对应的dom节点中,父应用和子应用异步加载。
加载完首个子应用后,开始加载其他子应用,并使用display: none将它们隐藏到页面dom结构中。最终dom节点如下图所示。
5.3 子应用iframe瞬间切换
解决问题:子应用切换卡顿
用户进行多个子应用切换时,擎天框架监听浏览器url地址,如pathname从/New/*变成/Web/*,则将/New/*对应的子应用iframe隐藏起来,将/Web/对应的子应用iframe显示出来,实现应用的瞬间切换。
用户可视范围内只能看一个应用,切换时仅仅是将New应用隐藏不可见,Web应用页面可见。
5.4 路由引擎,同步切换
解决方案:URL不同步
受vue2中数组方法(如push、shift)响应式处理的启发,擎天对前端路由框架进行特殊处理,重写了vue-router的push、replace等方法,当监听到子应用使用以上方法进行路由切换时,会同步到父框架进行操作。
父子应用简单配置后,父子路由同步便做好了。路由切换分为单应用以及多应用间路由切换。
(1)单应用路由切换
单应用子iframe路由切换,如/New/b 切换到/New/c,其pathname结构一致,是单应用的路由切换。子应用先切换路由,随后发送syncRoute给父框架,父框架使用replace方法改变浏览器地址栏即可,具体流程如下图所示。
(2)多应用间路由切换
如/New/b 切换到/Web/c,其pathname结构不一致。子应用先切换路由,随后发送syncRoute消息给父框架,父框架使用replace方法改变浏览器地址栏,同时将应用A隐藏起来,并发送消息到子应用B中。子应用B获得消息后修改其本身路由。
5.5 数据共享
解决问题:内存变量不共享
父应用将需要共享的数据放到store中,并使用syncStore进行注册。
子应用通过类vuex似的 mapGlobalState 方法可以获取父应用store中数据,同时该数据为响应式,数据变更可触发UI重新渲染。
通过mapGlobalMutations获取修改数据的方法,通过该方法可以修改父应用store数据,修改成功后擎天共享数据引擎将修改好的数据分发给各个子应用,保证共享数据一致。
其内部逻辑是:
父应用将需要共享的数据放到store中,并暴露getTopStore(获取store数据)、syncTopMutation(更新store数据)、syncTopStore(分发store数据)等接口。
系统加载时子应用通过getTopStore方法可以获取store中数据,并赋值到子应用$pstore中,从而获得数据响应式等能力。
当某个子应用需要修改共享数据时,调用syncTopMutation方法进行修改,修改成功后擎天通过syncTopStore分发给各个子应用,保证共享数据一致。
六、总结
擎天基于全屏Iframe与搭建公共组件等方式,解决了iframe UI不同步的难题,并充分发挥了iframe作为页面容器的优势,实现了父子应用异步加载、子应用瞬间切换的能力,从而达到单应用项目的体验。
基于 iframe 的微前端框架 —— 擎天的更多相关文章
- 极致简洁的微前端框架-京东MicroApp开源了
前言 MicroApp是一款基于类WebComponent进行渲染的微前端框架,不同于目前流行的开源框架,它从组件化的思维实现微前端,旨在降低上手难度.提升工作效率.它是目前市面上接入微前端成本最低的 ...
- 微前端框架 qiankun 技术分析
我们在single-spa 技术分析 基本实现了一个微前端框架需要具备的各种功能,但是又实现的不够彻底,遗留了很多问题需要解决.虽然官方提供了很多样例和最佳实践,但是总显得过于单薄,总给人一种&quo ...
- 微前端框架 之 qiankun 从入门到源码分析
封面 简介 从 single-spa 的缺陷讲起 -> qiankun 是如何从框架层面解决 single-spa 存在的问题 -> qiankun 源码解读,带你全方位刨析 qianku ...
- 微前端框架single-spa初探
前言 最近入职的一家公司采用single-spa这个微前端框架,所以自学了此框架. single-spa这个微前端框架虽然有中文文档,但是有些零散和晦涩. 所以我想在学习之余,写篇博客拉平一下这个学习 ...
- 微前端框架 single-spa 技术分析
在理解微前端技术原理中我们介绍了微前端的概念和核心技术原理.本篇我们结合目前业内主流的微前端实现 single-spa 来说明在生产实践中是如何实现微前端的. single-spa 的文档略显凌乱,概 ...
- 基于thrift的微服务框架
前一阵开源过一个基于spring-boot的rest微服务框架,今天再来一篇基于thrift的微服务加框,thrift是啥就不多了,大家自行百度或参考我之前介绍thrift的文章, thrift不仅支 ...
- 基于.NET CORE微服务框架 -surging的介绍和简单示例 (开源)
一.前言 至今为止编程开发已经11个年头,从 VB6.0,ASP时代到ASP.NET再到MVC, 从中见证了.NET技术发展,从无畏无知的懵懂少年,到现在的中年大叔,从中的酸甜苦辣也只有本人自知.随着 ...
- 基于Spring-Cloud的微服务框架设计
基于Spring-Cloud的微服务框架设计 先进行大的整体的框架整理,然后在针对每一项进行具体的详细介绍
- iframe & sandbox & 微前端
iframe & sandbox & 微前端 沙箱,容器,隔离 sandbox demo svg progress bar https://stackoverflow.com/ques ...
随机推荐
- 一个bug肝一周...忍不住提了issue
导航 Socket.IO是什么 Socket.IO的应用场景 为什么选socket.io-client-java 实战案例 参考 本文首发于智客工坊-<socket.io客户端向webserve ...
- Python教程——常用的os操作详情
Python自动的os库是和操作系统交互的库,常用的操作包括文件/目录操作,路径操作,环境变量操作和执行系统命令等. 文件/目录操作 获取当前目录(pwd): os.getcwd() 切换目录(cd) ...
- 爬虫(14) - Scrapy-Redis分布式爬虫(1) | 详解
1.什么是Scrapy-Redis Scrapy-Redis是scrapy框架基于redis的分布式组件,是scrapy的扩展:分布式爬虫将多台主机组合起来,共同完成一个爬取任务,快速高效地提高爬取效 ...
- Linux操作系统(6):进程管理和服务管理
进程的基本介绍 1)在 LINUX 中,每个执行的程序(代码)都称为一个进程.每一个进程都分配一个 ID 号. 2)每一个进程,都会对应一个父进程,而这个父进程可以复制多个子进程.例如 www 服务器 ...
- 基于ABP实现DDD--聚合和聚合根实践
在下面的例子中涉及Repository.Issue.Label.User这4个聚合根,接下来以Issue聚合为例进行分析,其中Issue聚合是由Issue[聚合根].Comment[实体].Iss ...
- SpringMVC底层——请求参数处理流程描述
在DispatcherServlet.java的doDispatch方法中,springmvc通过handlermapping里面找哪个handler能处理请求,handler封装了目标方法的信息, ...
- 跟我读论文丨Multi-Model Text Recognition Network
摘要:语言模型往往被用于文字识别的后处理阶段,本文将语言模型的先验信息和文字的视觉特征进行交互和增强,从而进一步提升文字识别的性能. 本文分享自华为云社区<Multi-Model Text Re ...
- 解气!哈工大被禁用MATLAB后,国产工业软件霸气回击
提起哈尔滨工业大学,相信很多人都不会陌生. 它是中国顶级的C9院校之一,从1920年建校的百余年来,哈工大一直享誉"工科强校"的美称,因其在航天领域的不凡成就,更是被人们誉为&qu ...
- 说起分布式自增ID只知道UUID?SnowFlake(雪花)算法了解一下(Python3.0实现)
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_155 但凡说起分布式系统,我们肯定会对一些海量级的业务进行分拆,比如:用户表,订单表.因为数据量巨大一张表完全无法支撑,就会对其进 ...
- Vue 路由的一些复杂配置
1 # 一.路由的props参数 2 export default new VueRouter({ 3 routes:[ 4 { 5 name:'guanyu', // 命名路由 6 path:'/a ...