单页系统前端MVC设计 – 总览

这是近期可能会接触的设计,对应的需求大致是:

  • 单页系统,所有与后端交互通过AJAX进行。
  • 每个模块、功能有自己的独立界面。
  • 不同模块、功能对应着不同的URL,要求能通过URL直接定位到相关的模块或功能上。
  • 模块间相互穿插,一个功能可能会突然被另一个模块引入,比如放在Dialog中。

对于上面的需求,我们讨论认为,对应的设计至少要能满足以下条件:

  • 基于URL的可导航性可访问性
  • 能够非常清晰、明确地划分模块及功能,划分上模块、功能相互独立
  • 可以方便地引入另一模块或功能的相关界面、交互。

基于此,虽然有着非常严重的过渡设计的嫌疑,最后我们还是选择了MVC的模式。当然这也是因为已经有过一个项目使用类似的模式,并且需要重用其中的一部分功能。

这一篇主要介绍整个设计的总体结构,以及一个请求的流程,首先下面是总体设计的总览图:

MVC框架总览

图很复杂,组件很多,所以说这根本就是个过渡设计的东西。但是考虑到我们讨论了N个小时确定的未来可能的进化路径,以及业务相关的变化点,这里绝大部分的组件还是有存在的必要性的,在后面几篇详细解说中会一一说明。

在这个设计中,一次流程大致是这样走的:

  1. HashListener组件会监听URL中hash部分的变化。
  2. 通过用户显式调用LocationManagerredirect函数,或者HashListener发现hash变化时触发LocationManagerredirect
  3. LocationManager会根据URL来组装一个Request对象,包括了访问的路径、相关的参数等,并将这个Request对象交给Controller,即调用ControllerprocessRequest函数。
  4. Controller将URL交给ActionMapping组件,要求其寻找对应的Action
  5. ActionMapping通过RegionUrlMatchDefaultRule三种策略来寻找正确的Action并返回。
  6. Controller得到Action后,先将原先保留的currentAction销毁,再调用新的Actionexecute函数,同时将该新的Action实例通过currentAction保留引用。
  7. Action在进入生命周期后,首先通过DataProxy来获取数据。
  8. DataProxy会与后端的Model进行交互(通常通过AJAX),并将Model返回的内容通过事先配置的ValueConverter转成前端需要的数据结构,此时的数据称为标准的Model
  9. Action通过ValueStack组件,基于自身的配置,将刚取回的Model转换成特定结构的,适应后面View模块的View Model。这里借鉴了MVVM模式
  10. ValueStack在组装View Model的时候,会需要一个ModelDescriptor作为View Model的结构的参考。同时还需要与SessionAppContext两个组件进行交互,因为有部分数据是存放在Session或者Application级别的。
  11. Action在取回ValueStack组装完成的Model之后,将Model交给Page对象。
  12. Page对象掌管着View模块,会生成一个控件树(ControlTree),并将Model绑定到各个控件上。
  13. Action可以通过Page对象暴露的事件、或者接触ControlTree中的具体控件来实现交互。

总体结构就到这里,在后面的系列中,会分以下几块详细介绍各个组件:

  1. LocationManager和HashListener
  2. ActionMapping过程
  3. DataProxy和ValueConverter
  4. ValueStack和ModelDescriptor,Session和AppContext
  5. Action与View的交互。

对于每一个组件,会争取从以下几个方面来说明:

  1. 需求及功能。
  2. 输入/输出接口。
  3. 简单实现原理。

世事变迁,14年回头看看这一系列的文章,现在支撑着整个百度数十个系统的MVC框架虽有参考此处部分的核心思想,但在设计上已经前进了好远,值得自豪也值得感叹。