Skip to content

Commit

Permalink
fix: fix some typo in blog (#648)
Browse files Browse the repository at this point in the history
Co-authored-by: zhou.cee <[email protected]>
  • Loading branch information
zqqcee and zhou.cee authored Nov 8, 2023
1 parent be01235 commit 8cf428f
Showing 1 changed file with 5 additions and 16 deletions.
21 changes: 5 additions & 16 deletions website/docs/blog/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ title: 微前端架构设计
slug: /blog
order: 1
---

微前端是什么:微前端是一种类似于微服务的架构,是一种由独立交付的多个前端应用组成整体的架构风格,将前端应用分解成一些更小、更简单的能够独立开发、测试、部署的应用,而在用户看来仍然是内聚的单个产品。

微前端诞生在两个大的背景下,在提倡拥抱变化的前端社区可以看到新的框架、技术、概念层出不穷,并且随着 Web 标准的演进,前端应用已经具备更好的性能、更快的开发效率。但随着而来的是应用的复杂程度更高、涉及的团队规模更广、更高的性能要求,应用复杂度已经成为阻塞业务发展的重要瓶颈。
Expand All @@ -12,7 +11,6 @@ order: 1

![image](https://user-images.githubusercontent.com/27547179/165336394-c724ee52-01a6-4667-9765-2b6c95c07ca6.png)


## 大规模 Web 应用的困局

尽管 Web 应用的复杂度和参与人数以爆炸式的增长速度,但却没有一种新的架构模式来解决现有的困境,并同时兼顾 `DX`(developer experience)和 `UX`(user experience)。
Expand Down Expand Up @@ -101,15 +99,13 @@ order: 1
![image](https://user-images.githubusercontent.com/27547179/165336487-d02e8b09-239b-4108-8351-de33d8f3bb1c.png)
![image](https://user-images.githubusercontent.com/27547179/165336535-8939d56e-0244-41d5-9d1c-b103d8743cc4.png)


### 微前端部署平台

部署平台作为微前端研发流程中重要的一环,主要提供了:微前端的服务发现、服务注册、子应用版本控制、多个子应用间同灰度、增量升级子应用、下发子应用信息列表,分析子应用依赖信息提取公共基础库降低不同应用的依赖重复加载。
用于解决微前端中子应用的独立部署、版本控制和子应用信息管理,通过 Serverless 平台提供的接口或在渲染服务中下发主应用的 HTML 内容中包含子应用列表信息,列表中包括了子应用的详细信息例如:应用 id、激活路径、依赖信息、入口资源等信息,并通过对于子应用的公共依赖进行分析,下发子应用的公共依赖,在运行时获取到子应用的信息后注册给框架,然后在主应用上控制子应用进行渲染和销毁。

![image](https://user-images.githubusercontent.com/27547179/165336565-b18887cf-8069-4bd1-b12a-3e80bc4277ca.png)


### 微前端运行时

> Why not iframe
Expand Down Expand Up @@ -157,7 +153,6 @@ order: 1

![image](https://user-images.githubusercontent.com/27547179/165336609-b903adc8-7978-4776-a966-104a251e0555.png)


### 应用生命周期

整个微前端子应用的生命周期基本可以总结为:
Expand All @@ -178,7 +173,6 @@ order: 1

![image](https://user-images.githubusercontent.com/27547179/165336638-d344a74b-ec15-43c1-958e-5dcec9e045fc.png)


### 加载器的设计

加载器的整体设计理念其实与 `React-loadable` 非常类似,具备以下能力:
Expand All @@ -200,7 +194,6 @@ order: 1

![image](https://user-images.githubusercontent.com/27547179/165336727-3489f2e6-20a9-4722-89f8-a3291fab8569.png)


### 沙箱的设计

其实在过去的 Web 应用中是很少提及到沙箱这一概念的,因为组件的开发一般都会由研发通过研发规范来尽可能的去避免组件对当前应用环境造成副作用,诸如:组件渲染后添加了定时器、全局变量、滚动事件、全局样式并且在组件销毁后会及时的清除子应用对当前环境产生的副作用。
Expand All @@ -209,7 +202,7 @@ order: 1
以 Webpack4 JsonpFunction 为例
在 Webpack5 中提供了一个重要的功能就是 Module Federation,随着 Webpack 5 推出 Module Federation ,与 Webpack 4 发生变化的一个重要配置就是 JsonpFunction 属性变为了 chunkLoadingGlobal,并且由原来的默认值 webpackJsonp 变成了默认使用 output.library 名称或者上下文中的 package.json 的 包名称(package name)作为唯一值(webpack.js.org/issues/3940)。

为什么会发生这个转变呢,如果了解过 Webpack 构建产物的一定会知道 Webpack 通过全局变量存储了分 chunk 后的产物,用于解决分包 chunk 的加载问题。由于 Webpack 5 引入 Module Federation 页面中可能会同时存在两个以上的 Webpack 构建产物,如果还是通过是通过同一个变量存储要加载的 chunk ,必然会造成产物之间的互相影响。
为什么会发生这个转变呢,如果了解过 Webpack 构建产物的一定会知道 Webpack 通过全局变量存储了分 chunk 后的产物,用于解决分包 chunk 的加载问题。由于 Webpack 5 引入 Module Federation 页面中可能会同时存在两个以上的 Webpack 构建产物,如果还是通过同一个变量存储要加载的 chunk ,必然会造成产物之间的互相影响。

通过 Webpack 4 到 Webpack 5 支持 Module Federation 之后可以发现,在一个基础库尚未考虑默认兼容多实例的场景下,贸然将其作为多实例使用很可能会造成应用无法按照预期运行,更为严重的是你以为其正常运行了其实应用已经发生了严重的内存泄漏或不可预知的情况,倘若将 Webpack 构建产物的应用多次动态的在页面中运行,将会发现已经造成严重的内存泄漏,因为 Webpack 会增量的向全局存储 chunk 的变量上挂载模块以及依赖信息,简单来说就是每次执行 Webpack 构建的子应用代码都会向 webpackJsonp 数组 push 大量的数据,最终造成内存泄漏,直至页面崩溃。

Expand Down Expand Up @@ -241,15 +234,15 @@ order: 1

###### VM 沙箱

通过快照沙箱的最简化的核心实现后可以发现,它的设计理念依赖于整个代码的执行属于线性的过程,即:存储执行环境=>执行具备副作用的代码=>恢复执行环境,但在实际的场景中对于应用的划分并以页面为维度划分,同一个页面可能存在多个应用,所以它的执行顺序并非线性,可能同时存在多个快照沙箱的实例环境,也就是快照沙箱多实例,以下面代码举例:
通过快照沙箱的最简化的核心实现后可以发现,它的设计理念依赖于整个代码的执行属于线性的过程,即:存储执行环境=>执行具备副作用的代码=>恢复执行环境,但在实际的场景中对于应用的划分并非以页面为维度划分,同一个页面可能存在多个应用,所以它的执行顺序并非线性,可能同时存在多个快照沙箱的实例环境,也就是快照沙箱多实例,以下面代码举例:

![image](https://user-images.githubusercontent.com/27547179/165336842-861d7e28-8c61-476d-9eb0-cfee04415e09.png)

通过上面的代码可以发现,在同时运行多个快照沙箱实例时,在代码执行顺序非线性的场景下,并不能有效的收集和处理应用的副作用,也基于此快照沙箱无法使用在非线性呢多实例的场景中,因此也进一步推出了 VM(virtual machine) 沙箱。

维基百科关于 VM 的解释:在计算机科学中的体系结构里,是指一种特殊的软件,可以在计算机平台和终端用户之间创建一种环境,而终端用户则是基于虚拟机这个软件所创建的环境来操作其它软件。虚拟机(VM)是计算机系统的仿真器,通过软件模拟具有完整硬件系统功能的、运行在一个完全隔离环境中的完整计算机系统,能提供物理计算机的功能。

在 Node 中也提供了 VM 模块,不过不过不同于传统的 VM,它并不具备虚拟机那么强的隔离性,并没有从模拟完整的硬件系统,仅仅将指定代码放置了特定的上下文中编译并执行代码,所以它无法用于不可信来源的代码。
在 Node 中也提供了 VM 模块,不过不过不同于传统的 VM,它并不具备虚拟机那么强的隔离性,并没有从模拟完整的硬件系统,仅仅将指定代码放置在特定的上下文中编译并执行代码,所以它无法用于不可信来源的代码。

参考 Node 中 VM 模块的设计,以及 JavaScript 词法作用域 的特性,可以设计出 VM 沙箱,不过与传统的 VM 差异也同样存在,它并能执行不可信的代码,因为它的隔离能力仅限于将其运行在一个指定的上下文环境中。

Expand Down Expand Up @@ -289,10 +282,9 @@ order: 1

![image](https://user-images.githubusercontent.com/27547179/165336996-76db3051-3dc2-4cbf-9540-264db8bf9168.png)


#### 路由系统的设计

在于现代 MVC 的设计思想,前端框架的设计思想也一直在发生变更,现代 Web 前端框架提供的最经典的能力莫过于将 MVC 中的 Constroller 变为了 Router,目前几乎主流的前端框架都支持路由驱动视图,仅提供一个 Router Map 路由表,无需关注控制任何路由状态即可完成跳转后的路由更新。
在于现代 MVC 的设计思想,前端框架的设计思想也一直在发生变更,现代 Web 前端框架提供的最经典的能力莫过于将 MVC 中的 Controller 变为了 Router,目前几乎主流的前端框架都支持路由驱动视图,仅提供一个 Router Map 路由表,无需关注控制任何路由状态即可完成跳转后的路由更新。

通过微前端出现的背景和意义,可以了解到微前端主要是用于解决:应用增量升级、多技术体系并存、构建大规模企业级 Web 应用而诞生的。那么在基于 SPA 的微前端架构中也可以了解到,目前微前端主要是采用应用分而治之 + 动态加载 + SPA 应用的模式来解决大规模应用带来的一系列问题。在以组件为颗粒度的 SPA 应用中组件内部是不需要关心路由的,但是在微前端中主要通过应用维度来拆分,那么拆分的应用也可能是一个独立的 SPA 应用,那么此时主应用与子应用的关系如何编排呢?

Expand All @@ -305,7 +297,6 @@ order: 1

![image](https://user-images.githubusercontent.com/27547179/165337076-752ff44b-397f-4c02-a755-252e22ae4813.png)


> 不考虑任何路由处理的场景
假设存在一个 Garfish 站点,这个站点它是由主应用+一个子应用构成。由于 Garfish 采用的是 SPA 架构,子应用与主应用所处于同一个执行上下文,子应用的路由原样反应在主应用上。
Expand All @@ -324,7 +315,6 @@ order: 1

![image](https://user-images.githubusercontent.com/27547179/165337244-1649b3a5-0048-4bad-a836-247a95c0ceea.png)


Hash 和 History 路由模式
目前主流的 SPA 前端应用基本上都支持两种路由模式,一种是:hash 模式、另一种则是 History 路由模式,两者的优劣和使用并不在本文的讨论范围之内,这里仅做在微前端这种分离式开发模式下的介绍,在微前端这种分离式 SPA 应用开发的模式下该选择哪种路由模式,以及多 SPA 应用下他们的路由应该如何编排:
正常路由情况
Expand Down Expand Up @@ -394,9 +384,8 @@ Hash 和 History 路由模式
![image](https://user-images.githubusercontent.com/27547179/165337300-a82aa763-4c70-474f-a33a-dabc7843178b.png)
![image](https://user-images.githubusercontent.com/27547179/165337326-29590a81-4531-47a9-abf7-c1c3b647a2b8.png)


> 自动计算出子应用所需的 basename
当应用处于激活状态时,根据应用的激活条件自动计算出应用所需的基础路径,并在渲染时告诉框架,以便于应用间路由不发生冲突。
> 当应用处于激活状态时,根据应用的激活条件自动计算出应用所需的基础路径,并在渲染时告诉框架,以便于应用间路由不发生冲突。
![image](https://user-images.githubusercontent.com/27547179/165337379-43506a80-71e9-4219-a14d-0f05dd64cfcb.png)

Expand Down

1 comment on commit 8cf428f

@vercel
Copy link

@vercel vercel bot commented on 8cf428f Nov 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.