购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.5 微前端实现要点

无论我们是在学习某一门技术、应用某一个框架,还是在玩一个RPG游戏,往往都有两件事情需要特别关注:主线与要点。

以《塞尔达传说:荒野之息》这款游戏为例,该游戏有很多主线任务,林克的最终目标是打倒灾厄盖侬,拯救塞尔达公主。而除主线任务外,还有许多其他游玩内容,如神庙挑战、支线任务、地图探索等。

学习Vue 3也是如此。首先,需要了解Vue 3的生命周期,包括Vue 3运行时的关键步骤和核心环节。之后,可以进一步学习它的一些关键API。

换句话说,“点”与“线”共同组成了完整的事物面貌。通过“点”与“线”的连接,形成了我们的知识体系,也就是“面”。

2.5.1 微前端拆分思路

笔者初次涉足微前端领域时,一系列专业术语立刻吸引了笔者的注意:iframe方案、路由式微前端、微件化、微应用化、NPM方案、动态Script方案、Module Federation等。这些术语如同微前端世界的关键词,每一个都代表了实现微前端的具体技术手段。

面对这些技术点,笔者最初的困惑是:这些技术点虽然具体,但它们能否构成一个完整的技术方案?能否作为理论层面的指导呢?带着这些疑问,笔者开始深入探索微前端的本质。笔者认为,这些技术点更像是实现微前端的实现手段,而非深层次的理论指导。

直到后来,笔者接触到了“横向拆分”与“纵向拆分”这两个概念。

1.横向拆分

我们先来看横向拆分,如图2-2所示。

图2-2 微前端横向拆分图示

从图2-2中可以看到,整个页面由三部分构成:页头页脚、轮播图和官网详情展示,这是一个常见的官网页面布局。但这些部分是由三个团队独立开发,并最终将输出结果组装在一个页面上。这种方法提供了很大的灵活性,让我们在不同的视图中复用微前端提供了可能。

在笔者的理解中,横向拆分更倾向于模块化,尤其适用于面向消费者(To Consumer,ToC)的项目。试想一下,像腾讯视频、爱奇艺等视频播放网站,它们的核心功能,如视频播放和视频展示,通常具备完善的功能、丰富的内容以及多页面复用的特性。这些特性使它们非常适合通过横向拆分来优化微前端架构。

事实上,横向拆分的概念在第1章中已经提及,即模块化。我们可以将一个或多个核心功能视为一个模块,这样它们就可以灵活地应用到网站的各个部分。

横向拆分特别注重业务功能的复用性和网站的搜索引擎优化(Search Engine Optimization,SEO),因为对于ToC项目,SEO是一个必然的需求。由于需要组合多个模块、优化SEO,同时明确团队职责的划分,横向拆分在技术实现和团队协作方面,无疑面临更加严峻的挑战。

2.纵向拆分

接下来看纵向拆分,如图2-3所示。

图2-3 微前端纵向拆分图示

图2-3展示了两个页面,它们各自承担着不同的业务职责。一个是产品详情页,另一个是产品创建页。尽管业务功能不同,但它们的头部导航和侧边导航部分是共用的。这种由一个团队负责整个页面设计和开发的方式,被称为纵向拆分。在这种情境下,如何合理地拆分业务?可以借助领域驱动设计作为指导思想。

如果你经常开发单页面应用,纵向拆分比横向拆分更容易理解和实施。

还记得这句话吗?“前端在一定程度上是天然解耦的。”这是什么意思呢?通常在开发任务分配时,我们不会让一个人编写搜索框,另一人编写列表,然后再将它们组合在一起。相反,我们会让一个人负责整个页面的开发,另一个人负责其他页面的开发。这样,在项目合并时,各部分不会互相干扰或依赖。这种场景和拆分方式更适合面向企业的后台管理系统和SaaS类项目。

这类项目的业务领域通常明确、范围清晰,但业务逻辑和场景复杂多变。通过纵向拆分,可以聚合对某一领域有深刻理解的开发和产品团队,专注于单一领域,从而为整个项目的迭代和发展带来更大优势。

2.5.2 界限上下文

关于界限上下文,以及如何区分核心子域、支持性子域、通用子域等概念,笔者不想在此过多讨论,因为本书并非专注于领域驱动设计。如果读者对此感兴趣,可以自行学习。笔者更想探讨的是界限上下文在微前端领域扮演的角色及其作用。

界限上下文,按照笔者的理解,实际上是指我们应如何逻辑地拆分微前端。例如,我们可以基于业务范畴进行拆分。在一个典型的SaaS系统中,可以拆分成应收应付、订单创建、产品、仓储等子系统。而在面向消费者的网购软件或视频网站上,则可以基于复用性或功能性进行拆分,如购物车、产品列表、产品详情,或者视频播放器、视频详情等。

然而,有时某些理论可能并不那么实用。举个例子,假设有一个老旧的SaaS项目,其目录结构与业务领域完全不相关,完全是平铺的。但现在,领导希望采用新技术,将老旧项目整合进来,以便开发新业务,实现项目上的统一。还记得我们之前讨论的微前端应用场景吗?特别是关于增量升级的部分,现在我们就着手进行这样的工作。

那么,我们该怎么做呢?在拆分老旧的系统时,区分其所归属的业务领域,将耗费大量时间来分离子系统。本来老旧的项目已经非常稳定,无须更改。但如果我们这么做,可能会给测试和开发带来许多问题。因此,我们希望老旧系统保持不变,直接使用Nginx反向代理或iframe即可。没错,这是一个不错的选择,但同时也带来了问题:我们是否遵循了领域驱动设计?显然没有,但这却是一个必要且正确的选择。

通过这个例子,笔者想告诉读者的是,理论是指导实践的重要工具,但在很多情况下,我们并不一定要严格遵循理论。

2.5.3 组合

组合的概念实际上指的是我们如何拼凑和加载微前端界面。例如,我们熟知的Qiankun、Wujie等微前端框架都属于客户端组合的范畴。除客户端组合外,还有服务器端组合和边缘计算组合。

我们先来了解这样一个流程:当我们在浏览器中看到界面时,在此之前都发生了什么。简单来说,就是从服务器获取界面所需的资源,比如HTML、JavaScript(.js)、CSS等文件,然后浏览器解析并渲染这些资源,最终呈现出界面。为了更快地获取这些资源,在客户端和服务器之间,通常会有一个内容分发网络(Content Delivery Network,CDN)来存储前端的静态资源,从而加快资源的获取速度。

那么,我们再来理解一下关于组合的三种方式。

● 客户端组合:实际上,这一过程是通过JavaScript在客户端运行时动态加载微前端及其相关功能来实现的,从而确保所有操作均在客户端完成。

● 边缘侧组合:我们会在CDN层对视图进行组合,通过一种叫作ESI的类似于XML的标记语言来达成这一目的。

● 服务器组合:类似于SSR,在服务器运行时或编译时进行组合,拼凑微前端,从而生成最终的视图结果,并将完整的HTML返回给前端。这种做法的最大优点是提升客户端的体验,以及提供良好的SEO效果。

2.5.4 路由

在现代单页面应用如此普及的情况下,读者对于客户端路由想必并不陌生。在大多数情况下,我们选择的微前端组合方式会对应于相应的微前端路由方式。当然,这种情况并非绝对。例如,当我们选择服务器组合时,如果服务器承受的压力过大,可以把路由分发的任务交给CDN处理,也就是边缘侧路由。

这里稍微强调一下,CDN路由或CDN组合属于边缘侧的一种方案,但边缘侧并不仅限于CDN。边缘计算是一种分布式计算范例,它将计算资源和数据存储在接近最终用户的位置,以便提供更低的延迟和更高的性能。换句话说,为了增强客户端或减轻服务器压力的一些中间或额外的基础设备所提供的增强能力,都可以算作边缘侧。

无论使用哪种方法,在实际操作中,并不局限于单一的选择。也就是说,我们既可以在客户端进行组件的组合,也可以在服务器端进行部分组合。同时,可以实现客户端路由,针对某些特定路径直接从服务器端请求数据,或者通过内容分发网络获取数据。这种灵活性使得我们能够根据项目的具体需求和特点选择最合适的实现方式。

2.5.5 通信

从微前端的定义来看,理论上我们不需要子系统之间的通信,因为微前端的本质是独立自治,它不应与其他子系统产生任何理论上的通信和关系。然而,笔者也多次强调,理论并不总是实用,我们不必拘泥于理论。

尽管微前端的定义强调独立性,但在大多数实际场景中,微前端之间的通信是非常必要的。例如,共享登录状态、传递子系统间信息等。

如果你处于同域环境中,可以利用Web Storage的SessionStorage、LocalStorage或Cookie来共享登录状态。即使在跨域的情况下,我们也可以使用postMessage进行通信。

但是,假设你面对的是一个非自主开发的项目,也就是说,你想要接入别人的跨域项目,且无法获取源码或进行私有部署,那么是否还需要通信呢?

让我们开一个小小的脑洞。除上述方案外,我们还可以利用现成的框架,如Wujie、Qiankun等,来实现开箱即用的通信手段。

当然,除此之外,我们也可以自己实现一个简易的EventBus来进行数据共享。另一种常用的选择是通过URL的查询参数(query)进行通信。这种方案简单实用,技术难度不高。然而,如果你想要在微前端中实现一个通用的URL传递参数的方法,还需要深思熟虑。

最后,我们进行总结,在微前端中进行通信的可的方案包括:

● WebStorage

● Cookie

● PostMessage

● EventBus

● 自定义事件(即发布订阅模式)

● URL

● 其他(比如window.name等)

● 状态工具(如Vuex、Redux等)

2.5.6 隔离

隔离这个话题并非随着微前端概念的出现而诞生,它一直存在,并且一直困扰着开发者们。我们都希望拥有一个干净、不受干扰的环境来发挥我们的技术才能。然而,JavaScript变量可能会被后来的代码覆盖,CSS选择器的使用可能无意中降低了其他样式的优先级,这样的问题确实令人头疼。

于是,JavaScript的模块化方案从IIFE发展到规范化的ES Module,CSS在某些特定领域也拥有了自己的作用域。那么,在微前端的范畴下,如何应对JavaScript和CSS的隔离问题呢?

在原生的背景下,我们通常基于iframe或者Web Components作为微前端实践的选项,从根本上实现JavaScript和CSS的隔离,以解决因选型所带来的某些副作用。这种方案正是Wujie微前端框架的隔离解决方案。

当然,除原生方案外,我们还可以使用如BEM等命名空间的CSS命名方式来隔离CSS,或者通过Shadow DOM来原生隔离C8SS和HTML。

关于JavaScript的隔离,方案其实有很多,比如Webpack Module Federation、各种微前端框架等,但它们实现的核心仍然离不开基本的原理。以JavaScript为例,在运行时环境中,要实现隔离,无非就是模块化或者IIFE等基本的JavaScript隔离方案。 Rht33n0qa75R5YvoSIflVK+YoajNaFAvBJmCV/N+Rm0uMM1n0qxPIRu0+OnmlYfa

点击中间区域
呼出菜单
上一章
目录
下一章
×