微前端与微服务
在这两三年里,移动应用出现了一种趋势,用户不想装那么多应用了。而往往一家大的商业公司,会提供一系列的应用。这些应用也从某种程度上,反应了这家公司的组织架构。然而,在用户的眼里他们就是一家公司,他们就只应该有一个产品。相似的,这种趋势也在桌面 Web 出现。聚合成为了一个技术趋势,体现在前端的聚合就是微服务化架构。
对于后端服务而言,微服务提供了数据业务解耦的能力,降低了彼此依赖。而在前端微服务化上,则是恰恰与之相反的,人们更想要的结果是聚合,尤其是那些 To B(to Bussiness)的应用。
微服务是运行在各自系统上,控制各自对应的数据库,通过网络进行交互的后端服务。
不同点:微前端不直接访问数据库,存在与浏览器环境,通过浏览器内存进行组件间的交互。
相同点:独立开发独立部署;前端的DOM类比后端的数据库服务,只通过暴露出的服务进行修改。除了拥有者不允许别的服务直接进行访问修改。
价值
- 解决痛点:解决遗留系统,才是人们采用微前端方案最重要的原因
- 微服务架构来解耦服务间依赖。微前端架构来聚合多种服务产品。
- 单页应用做大后加载慢,维护,回归成本升高,构建缓慢
- 中后台有三方接入需求
特点
- 子应用支持多框架
- 子应用独立开发,发布Pre,迁移成本低
- 应用体验接近 SPA
- 独立部署与配置自动化
问题
- 依赖管理问题,由谁来加载依赖,公共依赖的管理和版本迭代。统一这些依赖的版本,引入新的依赖时都需要一一加入。
- 规范应用的组件及路由。避免不同的应用之间,因为这些组件名称发生冲突。
- 共享通用代码。这显然是一个要经常面对的问题。
- 制定代码规范,状态管理方案等规范化。
微前端架构

框架应用职责明确
框架应用只做两件事情:
- 系统整体 Layout 的设计
- 所有子应用的配置与注册
框架应用尽量避免包含具体页面的 UI 代码,如果框架应用做了过多的事情会带来以下问题:(反例:XSpace)
- 框架应用样式代码太多,会增加子应用和框架应用样式冲突概率
- 框架应用为子应用提供其他能力比如一些全局 API,会破坏子应用的独立性,增加相互的耦合
- 框架应用本质是一个中心化的部分,变更后原则上需要回归所有子应用,因此需要保证职责的简单,越简单的东西越稳定
- 框架的迭代对子应用产生过大影响
子应用的路由通过前缀约定管理
避免产生路由冲突,便于框架路由管理注册。
子应用路由管理,父子路由交互的设定方案。
应用间通信
- 状态共享:通过全局 Store 进行数据状态的管理
- 事件监听:通过事件总线机制
子应用生命周期
- bootstrap
- mount
- update
- unmount
样式脚本隔离
样式隔离
- CSS Module
- 自定义组件 prefix
- 子应用避免全局样式
- shadow DOM (问题点:挂载根下的组件样式问题,如 dialog 等)
脚本隔离
沙箱方案
- iframe
- 优点
- 简单,浏览器自带隔离沙箱
- 线/进程隔离,安全性高
- 缺点
- 交互复杂,成本高。postMessage 信息交互 0.1ms 一个来回,1s 仅允许~1000次交互
- 加载成本高(线/进程)
- 优点
代理 JS 沙箱
技术点:
- 通过 Proxy 代理 windows 对象,截获数据访问
- 通过
with(sandbox) {${script}}构建执行环境,通过 with 动态改变全局 this 访问(不用 eval,容易越界访问) - 通过 new Function 构建沙箱函数进行执行
注意:Symbol.unscopables:可以越界访问,代理时需注意
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19function compileCode(code) {
code = 'with (sandbox) {' + code + '}';
const fn = new Function('sandbox', code);
return (sandbox) => {
const proxy = new Proxy(sandbox, {
has(target, key) {
return true; // 欺骗,告知属性存在
}
get(target, key, receiver) {
// 加固,防止逃逸
if (key === Symbol.unscopables) {
return undefined;
}
Reflect.get(target, key, receiver);
}
});
return fn(proxy);
}
}
web worker


微前端流程
