Appearance
微前端
技术的模块化是几乎所有行业都可以找到的自然步骤。
给一个旧的想法起一个新名字是一个很容易引起人们兴趣的技巧,而且它很有效。
一个复杂的代码库可能会大大降低团队的交付潜力。
微前端是一种受微服务架构启发的新兴架构。它背后的主要思想是将一个单一的代码库分解成更小的部分,允许一个组织在自治团队之间分散工作,无论是组合的还是分布式的,而不需要降低他们的交付吞吐量。
在纸上的伟大架构经常没有被很好地转换到现实中去,因为创建者没有考虑到环境及其背景(如公司的结构、文化、开发者的技能、时间安排等)。
在讨论微前端之前,我们先看看更为流行的单页应用程序(SPA)的缺点:
SPA 的缺点
SPA 对于某些类型的应用程序有一些缺点。第一次加载的时间可能比其他架构的加载时间要长,因为我们下载的是整个应用程序,不仅仅是用户需要看到的内容,如果应用程序设计不当,下载时间可能成为杀死我们应用程序的杀手。特别是智能手机和平板电脑等移动设备上加载不稳定或者不可靠的连接时。
代码分割(Code splitting)和懒加载(lazy-loading)JavaScript Bundle 是比较常用的优化手段。
值得一提的是渐进式(progressive) Web Apps,简称PWA,PWA提供了一套基于 Service workers 的新功能。
Service worker 是浏览器在后台运行的脚本,它独立于网页,用于提供离线体验或推送通知等功能。
通过 Service works 创建缓存策略
如果资源被缓存并脱机可用,请在从服务器下载之前先返回它。如果它还没有在缓存中,请下载并缓存它以备将来来使用。非常简单,但是可用有效增强移动设备的用户体验
缺点是 SEO 不好
微服务的优势
- 故障只会影响单个服务
- 多个团队可以独立的工作
- 更小的部署
- 可以自由地选择框架和程序语言
- 初始发布时间较小
- 有明确的边界
微服务原则
- 模型围绕业务领域(Modeled Around Business Domain)
- 自动化文化,在不同环境中自动部署独立单元的健壮文化
- 隐藏实现细节(Hide Implementation Details)
- 分散治理能力(Decentralize Governance),允许一个团队根据面对的问题采取技术方向,而不是为整个系统创造妥协
- 独立部署
- 故障隔离,一个服务崩溃,其余的服务仍然能够使用
- 高可观察性
优点相对的缺点
——对于优点1而言,会增加调试的复杂性,并且依赖于这个服务的服务会表现得很奇怪。
——对于优点4而言,如果没有其他人能够维护某种小众语言编写的服务,我们很难去扩展开发规模。
微前端需要考虑的点
- 即需要共享资源,又需要样式隔离
微前端问题
- 性能
- 安全
- 知识共享
- 可靠性
- 用户体验
Web Components
Shadow DOM 可以隔离样式,但是无法隔离 JavaScript。
通过全局变量,我们仍然能够访问父级脚本的导入。这是 Web Components 的缺点。
iframe
虽然 iframe
元素可以有效地隔离样式和 JavaScript,但是 iframe
中的内容和外部通信是一个问题。
message 通信
HTML5 提供了 window.postMessage
函数
发送消息:
iframe.contentWindow.postMessage("Hello", "*");
TIP
iframe
元素的 contentWindow
表示 iframe
的 Window
对象
接收消息:
window.addEventListener("message", event => { })
TIP
Window 对象上有一个 message 事件,可以接收消息,event 对象有一个 data
属性表示消息内容,有一个 origin
属性表示消息的来源(URL)。
需要注意的是这种通信方式无法共享资源,只有字符串可以通过 postMessage 函数进行传输。虽然可以序列化为 JSON 进行传输,但是它们永远不能是具有嵌套循环引用或者函数的真对象。
Web workers 和 proxies
Web worker 和 iframe 通信方式一致,主线程和 Web worker 之间也是通过 message 通信。
性能篇
(一)资源缓存
(二)Bundle Size
确保流畅的用户体验的唯一方法是创建仅按需加载的更小的资产。
了解依赖关系对最终生成的包的大小很有意义,webpack-bundle-analyzer
包可视化了这一项工作。
(三)请求优化
SWR 这类库可以缓存请求结果,提高用户体验。
共享功能
2 种极端的共享策略:
- 将所有共享的功能和代码放在一个公共的库或者微前端中
- 不共享任何东西——每个微前端都必须复制或者提出自己的解决方案
第 1 种策略的优点是,每个微前端都可以只专注于其问题领域,缺点则是对大量共享代码库的更改是可能放生的,而这些更改可能是破坏性的。
考虑到松散耦合,我们知道策略 1 不是正确的方法。第 2 种策略由于所有东西都被盲目复制,一旦知道某些共享功能有 bug,我们就需要在各处修复它,从而导致巨大的开销。
有两种解决方法:
- 只共享稳定的核心功能,而不是共享所有可以共享的功能
- 使用策略 1,但仍然允许在微前端中覆盖共享的功能。
什么是可以共享的功能呢:
- 身份验证和授权
- 权限和权利
- 功能标识
- 基本用户信息
- 导航
- 登录
实现微前端最简单的方法
将开发分解为几个包,然后在构建时组合在一起。然而这是微前端的完全静态使用。
静态使用的优点:
所有的信息在构建时都是已知的。这就导致了可能的优化,更深入的集成和增强的检查。使用这种方法可以实现更快、更可靠的应用程序。
微前端的依赖管理
Sharing all or nothing
- 共享任何东西都会导致约束和潜在的错误
- 如果每个微前端包含所有的解决方案,那么整个解决方案会变得相当臃肿和相当缓慢。
如何决定那些依赖需要共享
通常情况下,你应该不共享任何依赖。
不共享依赖犯错误比共享依赖犯错误要更小。
确定共享依赖的基本准则:
- 它们应该有相当的尺寸 15KB~30KB,100KB也合适
- 它们应该至少被两个微前端使用
- 它们应该没有多个基于破坏性更改的常用版本
- 它们应该在渲染组件中扮演重要的角色
- 它们应该是技术的,而不是业务的
我们需要记住一件事:一旦添加了共享的依赖,可能会非常困难,甚至不可能删除。因此这是系统规模增长的事情。
膨胀的应用程序是大规模重写流行的原因之一。
样式隔离可以考虑的方案
- CSS Module
- CSS-in-JS
- CSS auto-prefixing
参考资料
- 《The Art of Micro Frontends》—— Florian Rappl
- 《Building Micro-Frontends》—— Luca Mezzalira