你好,游客 登录
背景:
阅读新闻

SparkUI:一个可供参考的前端开发实践

[日期:2017-08-29] 来源:InfoQ   作者: [字体: ]

  SparkUI是一套完整且灵活的前端开发解决方案。该方案基于React,由Modula应用状态管理框架、一系列可重用的前端组件、以及构建SPA所需的各类支持库组成。该方案重视可重用性、灵活性、可测试性以及开发效率,解决了前端社区常见的一些针对商业前端应用开发的痛点,如复杂状态、Side Effect,组件拆分等,更在工程实践、文档化、本身代码质量等方面达到较高标准,为前后端分离架构下的商业前端应用开发提供了坚实的基础。目前SparkUI已成功应用在FreeWheel的前端项目中。

  Spark UI的诞生和演进

  •技术选型

  FreeWheel产生升级原有前端框架、开始重新设计SparkUI的想法大约始于两年前。2015年7月之前,FreeWheel其实已有一套基于jQuery的前端开发框架,但由于当时jQuery的版本较老、技术栈陈旧、整个框架维护不佳,而且缺乏一套可供新同事学习的文档,再加之React的兴起,因此我们决定对原有前端框架进行升级。

  另一方面,纵观整个大局势下,也有越来越多的互联网公司都在转向React.js去开发前端组件,除了性能因素外,很大一部分原因是用jQuery去写很复杂的DOM操作,后期代码会变得越来越难维护。

  当时,JavaScript框架中的“当红者”除了React外还包括Angular、Vue等,我们的前端团队主要对React和Angular两者进行了技术选型的评估。评估的各项指标显示,React.js相对Angular.js的学习曲线更低,而且也较为符合我们当时的基本想法——使用组件化的设计思想,所以,基于React开发前端开发框架的行动也随之展开。

  FreeWheel新老前端框架对比

  •迭代升级

  FreeWheel真正开始启动SparkUI是在一年半之前,这一年多也经历了几次比较大的迭代,从最初的0.X版本到Spark 1.0的版本,大概是以下几个逐步过渡阶段:

  0.X版本主要是基于Flux的应用状态管理框架。因为React本身只是在MVC架构下的视图这一层,只能用来构建视图组件(View Component),本身并没有状态管理的能力,而顶层组件的状态也愈发难以维护,因此当时的React生态圈中,React+Flux这类比较经典的架构在FreeWheel之外的其他许多公司也得到更为频繁的应用。

  但随着FreeWheel前端应用过程中页面交互越来越复杂,组件繁多的同时,各个组件状态繁杂以及状态间存在诸多映射关系,因此Flux在状态管理中的表现也越来越满足不了我们的需求。当时开发社区中出现了新的状况管理的理念和技术Redux,所以在从0.X版本提升到Spark1.0的过程中,我们在状态管理设计的思想上也从Flux切换到了Redux。

  1.0版本之后已经全面引入了Redux作为状态管理的框架,但这并不意味着是完全照搬Redux的状态管理机制。实际上,FreeWheel仅仅是用到Redux基本状态的存取接口和一些基本工具,在此基础上,我们主要是运用到SparkUI框架的核心模块(其内部称之为Modula)来管理整个状态,这其中也引入了诸如对象树(Model Tree)这类的概念——因为在Redux中的状态并没有层级,而都是平行展开,但FreeWheel的应用中,一个对象可能存在非常复杂的层级结构,所以需要引入Model Tree对应用数据进行集中管理(Modula相关设计理念将在后文中具体阐述)。

  各阶段中的应用状态管理框架对比

  除了状态管理框架的调整之外,1.0还引入了“函数式编程”的理念。所谓的“函数式编程”理念,是将在框架内部状态的流转完全用一种函数式的编程方式来实现。“函数式编程”最大的优势,就是把一个程序动态运行的过程以一种函数的方式来将其抽象。如果状态扭转过程是一个函数的话,其实能够保证传给函数的原始对象的状态不被改变,只是函数以新的状态输出。当一个应用比较复杂的时候,这样能够保证高效地跟踪和管理应用状态的改变。在SparkUI框架里,它也正作为一个基本的编程范式在使用。

  同时,社区对“函数式编程”中Side Effect的作用一直有不同的声音,但在实际应用中,我们发现Side Effect很难避免。比如说两个组件之间在操作上存在关联性——在一个Grid里操作完之后势必要影响另外一个Grid的展示或行为,这个过程我们也是通过Side Effect来支持的。

  因此可以说,Spark1.0 基本完成了对 0.X 版本的一次彻底的革新,但同时也导致原有产品中使用 0.X 的页面和应用需要切换到完全不同的API,这个过程也成为我们的一段重要经验。之后框架的升级改造,我们坚持的基本原则是,所有的改变都是以向后兼容(Backward Compatible)的方式修改,给应用方提供平滑过渡的过程。到目前为止,1.0 版本的整个框架处于相对比较稳定的状态,也已经在我们的生产环境里广泛使用。

  SparkUI架构整体解析

  SparkUI,可以理解为我们所谓的分层设计理念,整个SparkUI的架构和功能如下:

  •最底层就是React提供的API,主要提供了基本组件的创建,包括生命周期管理的API等等。

  •接着在此之上封装了Modula模块,Modula模块主要是做应用状态的管理,其本身还是使用了Redux来进行实际状态的存取和事件的分发, 并利用Immutable.js来保证对象树(Model Tree)的不可变性。

  •Modula之上提供了一部分状态管理中所用到的、做SPA(Single Page Application)所依赖的工具。

  •这层工具之上是SparkUI的组件库。因为FreeWheel当前主要以商业应用为主,其特点在于界面的变化和演进相对而言会更慢,但却特别强调新、旧界面间的高度一致性。所以我们在应用中抽象出了前端的组件库(比如有称之为Grid——高度定制化的一种表单的组件等),而所有的应用又会利用这些组件实现它们各自的功能。

  SparkUI框架

  •Modula模块

  SparkUI框架的设计过程中其实吸收了很多Redux状态管理的思想,现在也是使用了Redux来进行应用状态的存取和事件的分发,但和Redux最大的区别在于,状态管理复杂程度以及应用状态数量不同,其管理思路也具有一定的差异性。

  上文中提到的SparkUI框架核心模块——Modula实际上就是基于Redux(但并不限于Redux)的管理,它与部分Redux生态(如Redux-devtools)兼容,且已完整封装并隐藏了底层的Redux。下图简要介绍了Modula与React、Redux的关系:

  Modula应用状态管理框架

  例如,在Redux里,应用状态是完全平展开的结构且不存在任何的层级关系,因为缺乏一个对象化的组织,所以要在状态众多的情况下,在Redux的Store上找到某个状态就只能依靠纯记忆。而Modula引入了对象树(Model Tree)后,所有的状态都可以被对象化,即通过预先定义好的结构来组织状态。尽管是比较复杂的组件,在页面上的展示可能也只是一个表单或Table。

  如果给这个Table设定一个较为复杂的状态——加一个搜索条,搜索条本身有简单搜索和复杂搜索的区分,上面还有复杂的工具栏、动作条,其本身或许还需要支持翻页等。如此多的状态之下,用Redux的方式可能会有好几百个状态在一个Store里,于是管理起来就会非常困难;但Modula就可以组织得更好,下面是Modula主要的设计理念:

  •Application State=Initial State+Deltas,其中Delta是由Actions触发的(借鉴Flux, Elm);

  •Application State可以由一棵 Model Tree来描述,这棵树的每个节点都是一个可以描述有效业务实体的Model(借鉴Redux,Elm);

  •由一个给定的Application State到另一个State的Transition可以由Model Tree提供的 Reactions所描述,一次成功的Action到Reaction的匹配会将Model Tree演变为下一个状态(原创);

  •Side Effect是上述State Transitions的结果,它包含了一个更新的Model实例,以及零至多个Callback Functions(借鉴Elm);

  对于Modula中Side Effect问题的处理,Modula模块中的Receiver可以返回Side Effect,一个Side Effect可以是Sender或Bubble Event的引用,也可以是一段匿名函数(箭头函数);List-A读取完成时会根据List-A中包含的ID,自动触发读取List-B。

  所以目前在状况管理上,Modula相对于Redux会是一个比较适应复杂前端状态的应用改进。

  •前端路由框架Spark Router:

  此外,为了能够支持构建典型的SPA,我们开发了一个叫Spark Router的组件。它主要也是基于Modula,相比于React Router(其状态并不存储在Redux的Store上),Spark Router里的状态管理能够和应用中其他部分的状态管理采用同样的机制。

  此前,应用状态都分散在React Router的State与Modula Model(Redux state)里,两者经常遇到同步问题,我们的解决方案是将路由相关的State也合并进Modula中。因而,Spark Router 主要就是针对Model配置路由,Component可根据Model切换相应界面,这样就不必再在Spark Router的状态管理和应用中其他部分的状态管理之间添加同步设计,也让程序变得更简单。

  SparkUI当前的应用

  SparkUI目前已经在FreeWheel的生产环境中使用了超过一年的时间。我们内部几乎所有的UI产品都开始在使用SparkUI。但现阶段还仍处于从旧有实践过渡到新的基于SparkUI实践的过程。

  我们之所以会自己设计和搭建基于React的前端MVC解决方案,也在于FreeWheel的系统是广告资源管理系统,该系统的客户群体大多有非常复杂的工作流,并且是要通过UI来实现,因而导致了前端的应用状态多且复杂。所以,SparkUI框架的特点,或者说其应用场景即:擅长于用来构建有比较复杂的前端状态的应用。

  我们在实践过程中也借鉴了很多业界比较好的实践,包括Redux、Mobx等,而且能被重用的东西我们都尽量重用,比如Elemental这类的基础组件,这样可以在很大程度上降低使用者在SparkUI上需要的额外学习成本。此外,我们专门为SparkUI做了一个Documentation Portal,里面有非常丰富的演示能帮助需要使用SparkUI的同事来学习。目前,在FreeWheel内部,前端团队会定期给其他团队做使用交流和分享,并及时同步最新的一些改进。

  SparkUI还只是供FreeWheel公司内部使用,暂时并未开源。但据了解,FreeWheel和其母公司Comcast都对此非常鼓励,目前也已经开始对开源SparkUI走相关法务流程。FreeWheel首席架构师张晗表示:“SparkUI可能并不会全部开源,组件库这类属于产品特定需求的部分会被拿掉,像Modula这样的通用模型部分会属于开源的范畴。如果你的应用需要复杂的前端功能,特别是需要对具有相互关系的状态进行较多维护时,就可以考虑使用我们的Modula。”

  SparkUI的未来规划

  首先,我们目前需要对框架核心的升级进行性能优化。虽然React框架本身的性能也在不断优化,但它其实并不是以性能见长的前端框架,影响性能很重要的一点就是状态变化的计算,根据状态变化的计算来重新渲染这个页面。尤其当状态比较多的时候,此类检查就会比较费时。对此,我们会提出并引入一些标记方式,通过在对象树(Model Tree)上标识一个范围,只要在这个范围里的子状态被更新,服务状态所对应到的视图(View)就需要重新被渲染(Render)。

  其次,我们还在不断完善单页Web应用的资质。因为FreeWheel在做新的前端框架以及前端产品的升级过程中也同时在做前后端的分离。同时,我们的业务系统也做前后端的分离。所以说我们也希望把所有的展示逻辑、非数据逻辑都尽量地移到前端实现,最终整个应用可以变成SPA。目前,我们已经有对路由、国际化的支持,现阶段还在开发权限模块,目前的权限控制是在服务器端,接下来的目标是增强对权限控制、会话(Session)的管理。

收藏 推荐 打印 | 录入:Cstor | 阅读:
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数
点评:
       
评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款