介绍fusionjs:一个基于插件的通用Web框架

0
介绍fusionjs:一个基于插件的通用Web框架

一个鲜为人知的事实是,优步开发了很多基于网络的应用程序,实际上有数百个,而且还在不断增加。其中许多是用于管理业务各个方面的内部应用程序,而其他的则是面向公众的。

一个更广为人知的事实是,web技术变化迅速,最佳实践也在不断发展。为数百名web工程师提供具有现代功能的高质量框架,同时跟上web平台的动态特性一直是一个挑战。

为了应对这一挑战,优步的网络平台团队建立了Fusion.js,一个开源的web框架,它使web开发更容易,并产生轻量级、高性能的应用程序。

动机

随着网络行业最佳实践的发展,优步需要对其老化的整体网络框架进行改进,以应对多年来累积的技术债务带来的挑战。然而,我们也想让工程师们继续使用他们喜欢的技术(例如React和Redux),同时保持与优步应用健康监测基础设施的兼容性。

具体来说,我们希望核心框架能够解决以下痛点:

  • 复杂的配置和服务器端呈现、代码拆分和热模块重新加载所需的多个工具的样板文件
  • 缺乏良好的抽象来实现和共享涉及服务器呈现的React应用程序的不同方面的特性(例如,跨越服务器和客户端,处理序列化/水合,服务器-客户端通信等)。
  • 由于位于不同位置的代码的紧密耦合而导致的脆性
  • 由副作用和单因素引起的测试困难
  • 整体框架缺乏灵活性

虽然现有的解决方案解决了其中的一些挑战,但我们发现,将库粘在框架之上通常需要对多个不相关的文件进行更改。例如,在一个服务器渲染应用中支持Redux需要在服务器相关文件中添加设置代码,在浏览器中添加类似代码,在HTML模板中添加水合代码,在React Provider组件中添加等等。整合一个i18n库或浏览器性能指标库也会导致同样的问题。

让事情更困难,很多特定于应用程序的代码可以依赖库管理的副作用(例如,日志记录或数据持久性),和一个工程师很难整合图书馆这样一个可测试的方式没有的帮助服务层抽象。

虽然我们希望提供易于安装、经过测试的与Uber团队使用的各种库的集成,但我们也希望避免使用整体框架,以保持捆绑包的规模较小。

我们更喜欢模块化方法而不是现有的整体方法的另一个原因是,它迫使我们明确依赖关系,这使我们更容易避免常见的技术债务来源,例如上帝的对象、特别的内部接口和紧密耦合。

Fusion.js是我们努力的顶峰。

谁应该使用Fusion.js?

对于那些寻找开源样板文件来构建现代的、不平凡的web应用程序的人来说,Fusion.js是一个不错的选择。

简而言之,Fusion.js是一个mit授权的JavaScript框架,它支持像React和Redux这样的流行库,并具有像热模块重新加载、数据感知的服务器端渲染和bundle拆分支持这样的现代特性。

除了预先配置、优化的样板文件的明显优点之外,Fusion.js还提供了一个灵活的基于插件的架构。这使得它非常适合现代的单页应用程序和web应用程序,这些应用程序依赖于复杂的服务层来满足质量需求,如可观察性(例如跟踪日志、度量仪表板等)、彻底的测试(例如单元/集成/端到端的测试)和国际化。

关于Fusion.js的更多好处,请查看我们的文档

基于插件的架构

Fusion.js应用程序通用,这意味着应用程序只有一个入口点文件,并且可以在服务器和浏览器上重用代码。在通用应用程序中,React组件也可以在服务器上获取数据并呈现HTML,从而通过利用浏览器的原生HTML解析器,并避免JavaScript DOM API的开销,从而提高浏览器上的页面加载时间。

单入口点架构也使Fusion.js插件本身具有通用性,它允许插件开发人员基于代码所属的库(而不是代码运行的环境)共同定位代码片段。

图1所示。js插件基于逻辑分组封装逻辑,而不是基于需要添加代码的位置。

插件可以通过中间件访问HTTP请求生命周期,也可以访问React树来添加Provider组件。它们还可以初始化浏览器代码。

最终,这些特性使得只需一行代码就可以将库安装到应用程序中,而不管库需要多少不同的集成点。因为插件很容易添加和删除,所以在重构时也很容易推断插件的耦合性、对包大小的影响以及其他代码质量属性。它们还可以初始化浏览器代码。

类型的依赖注入

插件利用依赖注入,这意味着它们可以将定义良好的api作为服务公开给其他插件,并且在测试期间很容易嘲弄插件的依赖关系。当依赖关系负责与数据存储基础设施通信时,或者当它们与可观察性(例如,日志记录、分析和指标)相关时,这一点尤其重要。

也可以通过Flow.js静态地确保依赖之间的类型稳定性,如下所示:

图2。在代码编辑器中直接显示类型错误有助于在代码运行之前捕获错误。

中间件管理

多年前一个明显的挑战是流行的HTTP服务器库表达有一个API,它鼓励急切的副作用,这使得复杂的响应转换难以封装和测试。对于我们以前的体系结构,应用程序开发人员经常需要对Express请求/响应对象进行临时的monkey-patch,并小心地将不相关的关注点放在一起,这种方式只在需要按预期工作的顺序调用函数时才有意义。自然地,考虑到副作用丰富的子系统的高耦合时间需求,测试变得极其困难。

这个问题从Fusion.js的设计阶段就开始被关注。经过大量研究,我们选择雷竞技是骗人的了采用洋槐,它提供了一个更加单元测试友好的基于上下文的API,以及一个优雅的轻量级抽象,用于基于下游和上游概念的请求生命周期管理。

事实证明,Koa采用的设计决策很好地补充了Fusion.js中的设计决策。

Koa中间件为React Provider组件提供了一个逻辑集成点,并且下游/上游抽象与React服务器渲染上下文的生命周期完全一致。网络的副作用与应用程序逻辑解耦,提高了可测试性。

困扰我们老应用的上帝对象和操作顺序问题现在已经通过Fusion.js依赖注入和图形解析机制解决了。

图3。Fusion.js核心将网络副作用从应用程序状态中分离出来,并利用Koa和DI来实现子系统之间的松散耦合。

可测试性

在过去的几年中,JavaScript生态系统中出现了大量高质量的测试工具,人们对测试技术的认识也有所提高。

除了支持现代的测试工具,如开玩笑,,傀儡师, fuse .js还为开发人员提供了测试插件的工具。的fusion-test-utils包允许模拟服务器本身,使它能够在任意插件和模拟之间快速运行集成测试。

只是一个开始

自内部发布以来,在优步内部已经有超过60个仓库使用Fusion.js。我们预计,由于新的web项目和旧项目自动迁移到fusionjs的结合,这个数字会迅速增加。考虑到这一需求,框架级别的改进应该显著地提高这些项目的软件质量基线。

我们的路线图包括添加更多的性能优化和面向测试的工具,以及更好的Flow支持。

如果你对使用fuse .js感兴趣或者想要贡献,请查看文档和我们的Github组织。如果您有任何意见或问题,您也可以通过松弛

订阅我们的通讯以跟上优步工程的最新创新。

评论