❝解决拖延症分四步
- 把大目标化整为零
- 清空桌面
- 行动起来
- 及时奖励
❞
大家好,我是柒八九。
今天我们来谈谈一个比较时髦的概念- 「微前端」。
「微前端」是一套用于「组织大型前端应用的指导规范」。是受后端「微服务」启发而发展而来。
虽然微前端是一个备受关注的趋势性话题,但其中有一些概念是很难准确定位的。当理解一个新的趋势或技术时,首先需要理解它是解决哪些现存的问题。
接下来,我们就从微前端解决了哪些现存前端问题,并在解决这些问题的过程中做出了哪些取舍和权衡。
话不多说,我们开始。
微前端试图解决什么问题?
❝理解微前端的最简单的方法是「把它们看作是对大规模应用如何进行组织的一种技术解决方案」。 ❞
对于「大型应用」来说,有许多前端团队想要快速产生,并且这些团队能够在开发流程上「互相解耦」。这样他们就可以独立工作而不妨碍对方。
这在前端的实践中意味着什么。
- 「独立的可构建的前端资源」允许团队拥有属于自己的「构建流程」。这避免了冗长的构建时间,并防止个别团队被其他成员的误操作而影响项目构建。
- 「独立的可部署的前端资源」允许解耦的这些团队定期将他们的代码「独自」发布到生产环境,而不需要复杂的「部署顺序」。 这也意味着当bug出现和事故发生时,代码可以自动回滚,而不会干扰到其他团队的项目开发。
- 「清晰的所有权」在大型项目上是必要的。在大型项目中,如果某个功能不被维护,那几乎就像它不存在一样。因为没有人负责保持它的更新,它往往会变得难以维护。
这些是微型前端希望解决的一些「核心问题」。
如果解决得当,它可以加快团队的「开发速度」。这也是微前端被推崇的一个强有力的原因。
定义微前端
正如 「微服务」是后端关于如何构建服务的一种指导原则一样,微前端也可以用同样的方式看待。
「作为一套由特定技术实现的指导原则」。
有一些底层技术,如「webpack模块联盟」,使微前端成为可能。还有像single-spa
这样的流行框架,为实现微前端做出了不小的努力。
以下是摘自micro-frontends.org的微前端的原则,在此转述。
- 「技术不可知」
意味着团队可以选择他们想要的「任何前端技术栈」。但是建议将
WebComponent
作为通用的共享组件并对其进行标准化处理。 - 「隔离团队代码」 是指通过拥有「独立构建」的、能够「单独部署」的应用程序,使得团队能够解耦开发。
- 「当团队之间共享数据时,可以通过团队前缀来实现」
这意味着对「自定义事件名称」和诸如「本地存储」和
cookie
之类的东西要有一个名称间隔,以避免碰撞。 - 「优先使用浏览器功能而不是自定义API」。
这通常意味着优先使用「自定义元素」和
WebComponent
以及自定义事件进行微前端项目之间的通信。 - 「建立一个有弹性的网站」 意味着在建立你的微前端时要考虑到「渐进式增强」。
其中有些是可以解释的。这可能是设计上的问题,因为微前端并不指向一个特定的通用实现。这也是为什么对微前端的用途会让人感到困惑的原因之一。
与组件岛模式的比较
像Fresh、Astro和Marko这样的Web框架提倡组件岛模式Component Islands Pattern。
微前端在表面上和组件岛模式看起来很相似,都是将一个页面的渲染分割成多个独立的组件渲染。
但其背后的理念却截然不同。
❝「组件岛模式」的指导原则是
- 「默认使用服务器端渲染」而不是客户端渲染
- 只向客户端发送必要的互动内容。
❞
「重点是尽量减少Javascript的不必要的开销」。
这几乎是对传统的多页面架构的一种回归,在这种架构中,Javascript被镶嵌在服务器渲染的HTML页面上,使其具有一定的交互性。
在新的实现方案中,使用Preact/React/Vue
等现代技术代替jQuery
来编写页面逻辑,并封装成组件。
而,「微前端」都是为了「解决组织问题,而不是性能问题」。它被用在由许多团队合作的大型SPA的背景中。
微前端的潜在问题
❝微前端有着崇高的目标,「旨在解决真正的大型项目组织问题」。 ❞
然而,基于他们上面描述的理念,在实际开发中,还是需要在一些方面额外注意,以免造成不必要的问题。
潜在的性能隐患
在团队成员庞杂和项目应用比较复杂的情况下,让团队自由地选择他们想使用的技术栈,需要有一些重要的权衡。
在后端微服务的背景下。让团队选择他们的技术栈是有意义的。因为终端用户不需要下载和运行这些代码。
但在前端,情况就不同了。你最终会迫使用户下载和运行解决各种「常见问题」的代码,如样式设计、获取和改变数据、日期格式等。「以不同的方式,用不同的工具和框架,重复多次」。这是个很大的开销。
我们就不能把所有的东西都用「代码分割」吗?每个微前端都可以在页面加载时选择性地进行代码拆分。「代码拆分并不是银弹」。而且,页面往往是「瀑布式的组件加载」,然后获取数据,导致「更多的异步加载,这才是页面加载缓慢的真正原因」。尤其是在移动端,对「网络延迟」的敏感度往往更高。
困难的依赖性管理
即使通过同一个库来解决「功能共享」的问题,但是,在规模比较大的情况下,总是存在着该库的「不同版本」需要保持更新的问题。特别是对于像高优先级的更新。
多个重复的横向依赖导致用户多次重新下载同一代码(不同版本)。在有很多团队的情况下, 即使是使用同一个打包工具(webpack
)的情况下,这也是一个棘手的问题,如果每个团队都有自己的打包工具,那后果更是不堪设想。
这里简单说一下,为什么使用同一个打包工具,能够做到优化处理。
在前端工程化之构建工具中我们讲过,对前端资源先后经过了很多阶段,从刚开始的「纯手动」的资源解析和压缩,到后来基于「任务形式」的自动化处理。它们的主要目的都是对资源进行转换或者压缩,没法进行在项目级别的优化处理。到后来出现了基于「模块化」的构建方式,分析模块之间的「依赖关系」,从「入口模块」开始构建一棵「依赖图」,中间遇到的用到的 js、css、图片等都会作为他的依赖。然后对依赖图的每个节点分别用对应的编译器处理。
然后,由于有了各个资源的依赖关系,我们就可以做一些以往无法实现的优化操作。
Tree-Shaking
-有了依赖关系后,可以通过依赖分析,去掉一些没用的代码Code-Splitting
- 这些模块根据功能和类型拆分到不同的分组(chunk)里,然后生成不同的文件,然后将变更频繁和几乎不动的模块划分到不同的chunk
,并封装到特定文件中,针对几乎不会变更的资源和模块,则可以利用浏览器缓存进行资源的优化处理Lazy-Load
:生成的代码,拥有自己的runtime
,这样可以实现模块的lazy load
,也就是把Code-Splitting
分出来的 chunk,在运行时「动态加载」
而关于webpack
如何实现资源查找和解析的,可以参考工程化之webpack打包过程
服务的粒度划分
类似于后端微服务存在的问题,前端微服务也有类似的困惑。微服务的划分粒度如何判定?
如果我们把事情分解得太多,我们就有可能产生依赖关系,需要我们把多个前端部署在一起。这就违背了,我们使用微服务要解决前端服务可以独立构建和部署的初衷。
回顾总结
❝「微前端」都是为了「解决组织问题,而不是性能问题」 ❞
后记
「分享是一种态度」。
参考资料:
- 工程化之webpack打包过程
- 你能给前端工程化下个定义么?
- micro-frontends.org
- 前端工程化之构建工具
- understanding-micro-frontends