一、前言1.1微前端的含义在开发一个系统的初期,我们可以将所有的代码放在一个项目中。随着企业的发展,业务逻辑越来越复杂和专业化,不同的研发团队会被细分,独立负责其中的某一部分。每个开发团队都有自己的迭代节奏,很难在同一个耦合项目中满足所有团队的需求。我们很自然地会将整个系统拆解成多个子应用/子项目,可以独立开发,独立部署,但它们协同工作,支撑着系统的整体功能。当上述系统拆解过程发生在后端时,称为微服务;当它发生在前端时,它被称为微前端。从某种意义上说,微前端是默认的,不需要额外的努力。浏览器从一开始就实现了超链接,支持在多个HTML页面之间跳转。英国科学家蒂姆·伯纳斯-李(TimBerners-Lee)于1989年在CERN工作期间发明了万维网(WWW)。最初构想和开发Web是为了满足世界各地大学和研究所的科学家之间对自动化信息共享的需求。1989年,英国科学家TimBerners-Lee在CERN工作期间发明了万维网(WWW)。万维网最初的构想和开发是为了满足世界各地大学和研究机构的科学家之间自动共享信息的需要。自发明以来,Web一直是一种用于跨团队(不同大学、不同科学组织)进行交流和协作的信息技术。但是简单的页面跳转往往会在页面过渡阶段出现白屏,在体验上无法满足我们的需求。因此,当我们说“微前端”的时候,我们要实现的是:不同的前端团队可以独立开发和部署自己的应用,满足自己的迭代需求多个前端子应用之间的协作和切换,应该有没有不可接受的用户体验下降1.2微前端的类型我们可以根据拆解的粒度来划分微前端:页面级微前端(page-level):每个子应用都有一个专属的页面,切换betweenpages是页面之间的跳转/切换。区域级微前端(section-level):在同一个页面中,有两种类型的区域:a.共享区域,如顶部菜单栏、侧边栏等,是所有子应用共享的。b.切换区域通常作为主要内容呈现,子应用在该区域进行部分切换。页面级微前端(page-level)是浏览器默认功能,但体验不好;因此,目前的微前端框架大多是专门针对区域微前端(section-level)的,代表框架有Qiankun、Single-Spa等。区域微前端的主要实现思路大致可以概括为:代理或劫持窗口环境,让多个子应用及其依赖的前端框架可以独立运行,互不干扰。每个子应用都注册了“创建”和“销毁”等生命周期,等待主应用根据url来驱动和调度它们。段级微前端(section-level)可以很好的解决某类微前端场景(比如复杂的后台系统),而子应用恰好有相同的界面风格,甚至相同的Layout,比如和顶部菜单栏、侧边栏等模块一样,只是主要部分的内容有所不同。但是在其他场景下,我们可能还是需要页面级的微前端(page-level)。子应用程序具有不同的UI样式甚至不同的布局。它们之间的切换是整个页面的切换,不是局部切换。我们不希望子应用为了满足区域微前端(section-level)的接入需求而做巨大的调整甚至改变开发方式。子应用需要同时存在,切换过程中可以用滑入/滑出动画过渡,回滚过程中可以自动保持滚动条的位置。etc.今天要介绍的——零界微前端属于上述页面级微前端(page-level),克服了子应用切换过程中的体验问题。2.零边界介绍2.1设计理念成本可控。访问成本不应随着访问的应用程序数量的增加而呈指数增长。访问2个应用和访问100个应用的注意事项应该是一样的。技术上确实无关紧要。无论应用使用什么技术栈,渲染方式是SSR还是CSR,应用类型是SPA还是MPA,都可以无缝对接。零耦合。微应用与主应用、微应用与微应用之间完全没有依赖关系。应用程序的访问和退出不会对应用程序本身和已经访问的应用程序带来任何副作用。2.2基本工作原理作为页面级微前端(page-level)的解决方案,零界与版块级微前端(section-level)在架构上大致相同,只是在实现上有所不同。零世界采用经典的基础应用+配置方式管理子应用。在零界,基座也被称为炮弹。shell只做两件事:存储微应用和调度微应用。所有微应用都加载在iframe中,零界通过shell管理多个iframe的加载和切换。但是,iframe可能会导致路由不同步的问题。零界通过pushState、replaceState等历史API,将当前激活页面的地址同步到浏览器地址栏中的位置,保持URL一致。与市面上的微前端框架最大的区别在于,零边界没有生命周期、EventBus等复杂的概念,而是监控微应用的跳转行为,并将所有的跳转记录存储在浏览器中。微应用程序链接在一起。每次微应用跳转,都会以iframe的形式加载一个新的页面到零边界微前端,不会立即释放上一个微应用的内存,可以回滚迅速地。为了避免iframe过多导致页面卡顿,ZeroBounds限制了iframe的最大数量。特点:无需修改原有代码。技术栈无关,不用担心前端开发难度。访问成本几乎为零。每个页面只需要引入一个脚本文件就可以加入零边界微前端机制。无需刷新即可切换页面。提供无刷新页面切换的SPA体验,给用户一致的体验。安全可靠。所有页面都可以随时退出零边界微前端机制,回到原来的状态。状态同步。刷新页面不会丢失路由状态,页面回滚显示的更快,并且会保留上一页的滚动条和页面状态。完美隔离。每个页面的css和js完全隔离,避免了应用之间的变量污染。2.3为什么要用iframe来搭建版块级微前端?由于iframe的易用性和自身的进程级隔离,很多开发者都考虑过使用iframe构建微前端,但最终都放弃了这种方案。.下面结合下图回顾一下使用iframe搭建一个section-level的微前端(section-level)可能会出现的具体问题。区域微前端页面示意图(1)DOM碎片化严重。遮罩只能覆盖其中一个微应用(蓝色区域),不能覆盖整个应用(整个粉色区域);(2)沟通困难。一个页面同时存在不同的微应用,微应用之间需要额外的通信,而iframe只能通过postmessage传递序列化的消息,不能满足需求;(3)加载慢。一个页面上通常会有多个微应用,微应用会被频繁的挂载和卸载,而iframe的每一次加载都是对context的重构;(4)路由状态丢失。刷新页面后,iframe会回到第一次加载时的状态;可以看出,这些痛点是iframe的特性造成的,不仅仅针对版块级微前端(section-level),而是使用iframe时的普遍性问题。现在,我们从页面级微前端(page-level)的角度,对以上问题一一思考:零边界微前端页面示意图(1)DOM碎片化严重;不用解决?如上图所示,所有的微应用都是全屏显示的,不存在遮罩层不能全局展开的问题。(2)沟通困难;无需解决?每个微应用本来就是一个完全不同的应用,所以不需要关注应用之间的通信。(3)加载缓慢;不用解决?在页面级微前端(page-level),每次进入页面只会加载一个微应用(iframe)。(4)路由状态丢失;该问题也存在于页面级微前端。也就是说,我们只需要解决浏览器历史同步的问题,就可以最大限度的利用iframe的特性。这也是为什么零界选择iframe管理微应用的原因。3、使用方法3.1基本使用如上图所示,假设我们现在需要实现如上图所示的首页、A页、B页、C页四个页面切换不刷新的效果,应该如何实现它?如果是同一个应用的不同组件,可以通过React或者Vue的TransiitonGroup等组件快速实现。但是,如果是4个naiveHTML页面/应用,可能很难通过传统的前端框架来实现,甚至大部分区域级的微前端(section-level)都无法完成。在零界中,每一个微应用都是全屏的,存储在一个iframe中,通过操作iframe就可以对微应用进行操作,就像在普通的DOM元素上叠加样式一样。零世界为H5页面模拟了NativeApp中WebView的切换机制,也就是上图中的切换效果,连接零世界即可开箱即用。让我们来看看如何构建一个零边界的微前端。第一步是创建一个零边界壳。假设四个页面的地址是:localhost:3000/demo/index.htmllocalhost:3000/demo/pageA.htmllocalhost:3000/demo/pageB.htmllocalhost:3000/demo/pageC.html如上图,不需要通过npm/yarn安装不需要调用任何函数,只需要对一个普通的HTML页面做两处改动就可以完成shell的搭建:(1)设置连接到零的微应用的匹配路径边界。(2)引入零界shell脚本,即可获得零界能力。第二步是访问零边界。在4个应用的HTML中,分别在head标签中写入如下代码。我们只对接入零界的小程序做了两处改动:(1)配置开启/关闭零界。(2)引入零边界页面脚本。这就是构建零边界微前端所需的全部代码。这种接入有以下三个好处:(1)没有学习成本,直接介绍(2)不影响应用本身的SEO(3)在Zero中作为子应用运行和独立运行应用没有区别Boundary,它们的路由路径此外,我们可以发现,当微应用无法匹配到shell中配置的路径,或者微应用关闭零边界时,是无法访问零边界的。因此,当应用连接到零边界后无法正常访问时,可以通过配置远程关闭零边界,页面退化为正常页面,无需等待shell更改配置。而且,这既不会影响零世界已有的微应用重定向,也不会影响零世界的微应用重定向到这个页面。3.2零边界进阶以上展示了简单页面的切换,体验了H5页面零边界滑入滑出的效果。但是,普通页面不能满足我们的实际需求。想象这样一个场景:有多个CSR应用,它们共享同一个Sidebar,但是有不同的Content,直接显示时都会白屏。我们希望消除切换时的白屏,直接看到更完整的内容。页。这是一个常见的B端项目优化需求,版块级和页面级微前端都可以提供解决方案。在现代Web开发模式中,页面中的内容通常会根据功能和区域划分成不同的组件,以提高代码的可重用性和可扩展性。所以Sidebar和Content可以看作是两个不同的组件。版块级微前端(section-level)和页面级微前端(page-level)对应用中组件的处理方式不同,导致优化策略不同:版块级微前端(section-level)以组件(Area)为单位,拆分原有应用,重构组件。之后,我们会从组件的角度考虑如何在基础应用中主动挂载和卸载,以达到预期的效果。页面级微前端(page-level)以页面为单位,在不改变原有应用组件的情况下,聚合所有应用。所以聚合之后,我们会考虑如何从应用的角度被动优化内部组件。通过区域微前端来解决,大致分为4步:(1)在每个应用中拆分Sidebar和Content。(2)将每个Content单独部署为一个微应用,配置基本信息,添加生命周期。(3)将Sidebar直接放入dock应用中,或者作为微应用单独部署。(4)创建一个基础应用程序并注册所有的微应用程序。切换应用时,只需要卸载上一个应用的内容,加载下一个应用的内容即可。sharedSidebar部分没有变化,完全模拟了应用中的切换体验。但由于风格隔离、运行性能、子应用保活等因素,目前市面上现有的区域级微前端(section-level)方案很难找到完全满足需求的方案。而且无论需要什么样的改造方案,改造的成本都是比较高的,而且这个成本会随着应用的数量呈指数增长。看看页面级微前端是怎么解决的。零边界提供了另一种思维方式,在不侵入更改原始应用程序的情况下优化应用程序之间的交互。改造分为2步:(1)创建零边界shell并配置访问微应用的路径(2)将零边界页面脚本引入所有连接的应用程序至此,效果与之前展示了简单的页面切换,但是页面跳转还是造成了碎片感。为了提升用户体验,在零世界前端切换页面时,会在顶部显示一个进度条,提示页面切换的进度。而且零边界受到Puppeteer和Playwright这两个e2e测试工具的启发,在里面模拟了waitFor函数,如上图,意思是等待Sidebar组件显示在页面上,然后再切换页面。这样当多个应用在同一个Sidebar的页面之间切换时,Sidebar的部分在视觉上是固定的,只有Content发生了变化。这样就可以在多页面应用中获得身临其境的体验。不仅如此,你还可以通过timeout设置最大等待时间,一旦超过等待时间,页面就会被强制切换。这种优化方式带来了以下好处:应用内容和侧边栏的交互不需要额外的机制,因为它们本来就是同一个应用的不同组件。应用不必承载相同的Sidebar,随着业务发展需要更灵活的确定自己的UI,零边界不会成为应用扩展的瓶颈。下面对比一下优化前后的效果。为了更直观的感受差异,我们将网速调整为高速3G。没有任何优化,每个页面都是不同的应用(网速:高速3G)。零边界优化后(网速:高速3G),可以很明显的看到,经过零边界优化后,多页面应用的跳转更加流畅,并且支持快速返回页面。细心的读者可能会发现,两个动画的网址不一致。这里我们只展示零边界带来的优化效果,零边界跳转是通过本地Node代理服务器完成的,所以和应用的原始URL不同。在开发企业级项目时,通常不存在这个问题,可以通过SLB等方式快速解决。另外值得一提的是,零边界文档也是基于零边界微前端构建的,大家可以直接在MPA中体验零边界切换的效果。有兴趣的可以查看零边界文档。综上所述,我们已经介绍了零边界的基本原理和使用方法。其适用场景可以归纳为:资源整合。无需重构即可组合多个现有的大中型应用程序。MPA优化。提升从MPA到SPA体验的跳跃。H5终端体验优化。低成本通过框架的能力实现WebView的切换效果。零边界虽然是页面级的解决方案,但这并不意味着它与节级的解决方案冲突。如果你想快速体验微前端,或者你的项目因为现有的微前端框架成本太高还没有实现,你可以先尝试使用零界,在零界你可以随时退出而不会带来任何副作用。如果尝试后还是不能满足需求,再考虑接入更细化的区域级微前端(section-level)。
