模块化开发手册

概述

对于一些比较大型的单页面多模块系统我们需要一套解决方案便于系统的开发和维护,本文档针对这种情况提供一套解决方案,各产品中如有类似需求可选择使用此解决方案来处理,在阅读本文档之前请先参阅框架中模板使用手册。

结构 

整个方案纵向采用MVC分层架构思路,横向采用模块化调度思路,对系统的各子系统模块化封装,模块之间通过消息机制交互,示意图如下所示

img

方案主要由以下几部分组成:

  • 历史管理器:主要负责管理操作历史及系统对外接口变化检测
  • 模块调度器:主要负责按照接口变化调度相应的模块
  • 模块:主要负责实现具体模块的业务逻辑

各组成部分的相互关系如下图所示:

img

模块

模块主要负责完成指定的业务逻辑。一个系统可以由若干个模块组成,通过模块之间的有机协调完成完整的功能。模块之间可以是兄弟关系也可以是父子或者子孙关系,因此我们能可以将一个系统里的所有模块抽象为一颗树,而树上的每个节点都可以表示为一个模块,而节点之间的关系则用来表示模块之间的关系。当一个系统被拆分为若干个模块后我们需要有个标识能够用来识别指定的模块,因此我们在这里引入一个UMI概念,详细内容如下文档所示。

类型

系统中的模块可以分为两类

  • 访问模块:通过对外接口可直接访问到指定模块
  • 私有模块:此类模块多为可访问模块的组成模块,若干私有模块组成一个可访问模块

假设下图为一个可访问模块,则每个橙色框可视为一个私有模块,此类私有模块无法通过地址栏直接定位访问。

img

UMI

UMI(Uniform Module Identifier):模块统一标识符,用来唯一标识系统中指定的模块。

格式

UMI的格式为URI中path部分内容 UMI必须以“/”符号开始,私有模块标识必须以“/?”符号开始 简单的模块标识符如“/a/b/”

含义

UMI除了用来标识一个模块外,还承载了以下信息

  • 模块标识
  • 模块类型,UMI中一级模块名称是否为“?”来区分可访问模块与私有模块
  • 模块的层级关系,假设有三个模块的UMI为“/a”,“/a/b”,“/a/c”则表示b和c模块为a模块的子模块

结构

一个模块的主要组成部分包括:

  • 结构(html)
  • 样式(css)
  • 逻辑(js)

对应到本模型中,一个模块则由一系列的模板组成,对于模板的详细信息可参阅模板使用手册

  • 模块的结构部分通过三类基本模板类型引入,也可以通过html类型模板引入外联结构模板
  • 模块的样式部分通过css类型的模板引入
  • 模块的逻辑部分通过js类型的模板引入

实际应用中,我们会将一个模块封装在一个html页面中(当然你也可以将多个模块封装在同一个html页面中),页面由一系列的模板结构组成,具体内容可见如下图范例

img

基类

模块的基类定义在/util/dispatcher/module.js中的nej.ut._$$Module,所有模块必须从此模块基类继承,否则调度器将忽略注册的模块。

需子模块实现的主要接口有:

__onShow

img

__onApply

img

__onHide

img

__onRefresh

img

__onMessage

img

__onBeforeHide

img

##调度器 调度器主要负责根据接口变化情况调度呈现指定的模块,为了能够准确的进行调度,我们需要预先将系统中的可访问模块注册到调度器中,然后激活调度器(只有激活调度器才开始工作),不过理论上模块的注册只要在模块被实际调度前都可以。 载入此模型的库文件后,框架会在全局实例化一个调度器实例dispatcher,后续可直接调用此实例的接口完成相应的操作。

调度树

####概述 前面我们说模块的UMI承载了模块的父子关系,因此当模块注册到调度器的时候,调度器会根据UMI信息构建一棵模块的调度树。如下图范例所示,当我们注册一系列模块的UMI至调度器的时候会构建一颗相应的调度树,同时将注册的模块信息保存在对应的节点中。

img

说明

  • 所有私有模块均在调度树的一棵子树下,该子树以“?”节点为根节点,私有模块的调度流程同可访问模块的调度流程一致。
  • 私有模块通过调度器的应用接口生效而不会走对外接口调度的流程。
  • 不同路径下的同名节点不存在任何关系,如“/a”路径下的“a”节点同“/?/a”下的“a”节点没有任何关系。
  • 模块显示调度顺序从根到叶子,模块隐藏的顺序从叶子到根。即调度过程必须确保调度路径上注册了模块的节点被依次调度。

调度流程

主流程

当对外接口发生变化时调度器会接收到历史管理器传过来的变化事件,调度器根据当前需要调度的接口和调度树的情况按照如下图所示的流程进行模块的调度。 调度的主要思路:

  • 计算当前节点与目标节点的公共节点
  • 隐藏当前节点到公共节点之间节点的模块
  • 刷新公共节点到树根节点之间节点的模块
  • 显示目标节点到公共节点之间节点的模块

img

显示流程

调度模块显示时调度器会自动检测注册的模块是否需要动态载入,如果需要动态载入则会先根据注册的载入信息动态加载模块资源,当模块载入完成后再做显示调度。 显示调度过程中调度器在调度目标节点上的模块前会确保所有从目标节点到树根节点之间的注册模块做了相应的调度操作。 模块显示调度的主要思路:

  • 确保所有模块载入
  • 确保模块的父模块已被调度
  • 调度模块

img

消息机制

####概述 模型中定义模块之间的交互均采用消息机制,禁止模块之间产生直接的依赖关系。模块可以通过消息接口向指定UMI的模块发送消息,同时也可以通过实现自身的消息接口来接收外界的消息。至于具体消息的业务逻辑则由模块负责实现。 调度器提供三种消息方式:

  • 目标消息:消息只传送至指定UMI的模块,即仅此模块会收到消息
  • 目标广播:消息传送至UMI指定的路径上的所有模块,即路径上所有注册的模块都会收到该消息
  • 群体广播:消息传送至UMI指定模块的所有子孙模块(包括UMI路径上的模块),即所有UMI及路径以UMI开始的模块都会收到该消息

多个模块接收消息的顺序为:同一路径上从根节点到目标节点

举例

假设以下范例中所有节点均注册了模块 目标消息如下图所示

img

目标广播如下图所示

img

群体广播如下图所示

img

对外接口

_$regist

img

_$registAll

img

_$active

img

_$apply

img

_$redirect

img

_$message

img

_$rule

img

历史管理器

历史管理器主要负责侦测系统对外接口的变化,同时记录每次变化历史。

流程

历史管理器的操作流程相对比较简单,主要通过定时器监测地址的变化情况,如果发生变化则触发一个变化事件,主要流程如下图所示 img

接口

location.redirect

img

location.active

img

事件

location.onurlchange

img

应用举例

需求定义

img

模块划分

私有模块 序号 UMI 描述

  1. /?/sys/top/ 顶部区域功能模块
  2. /?/sys/left/ 左侧区域功能模块
  3. /?/sys/right/ 右侧区域功能模块
  4. /?/sys/footer/ 底部区域功能模块 模块层级关系树 根据给定的功能列表可将系统划分为以下对外模块 序号 UMI 描述
  5. /m/newest/ 新鲜主打
  6. /m/queue/ 当前播放列表
  7. /m/dj/ 专业DJ首页
  8. /m/dj/profile/ DJ基本信息
  9. /m/dj/show/ DJ节目详情
  10. /m/profile/ 用户基本信息
  11. /m/artist/ 艺人详细信息
  12. /m/album/ 专辑详细信息
  13. /m/playlist/ 歌单列表
  14. /m/search/ 搜索结果列表 模块层级关系树型图 img

    模块实现

    目录结构

    img

    系统入口

    img

    模块样式

    img

    模块结构

    img

    模块逻辑

    img
© 1997-2013 Netease. All Rights Reserved.
comments powered by Disqus