架构
模块化设计
thinkjs基于分组/控制器/操作的设计原则,一个典型的URL如下:
http://hostname:port/分组/控制器/操作/参数名/参数值/参数名2/参数值2?arg1=argv1&arg2=argv2
分组一个应用下有多个分组,一个分组都是很独立的模块。比如:前台模块、用户模块、管理员模块控制器一个分组下有多个控制器,一个控制器是多个操作的集合。如:商品的增删改查操作一个控制器有多个操作,每个操作都是最小的执行单元。如:添加一个商品
项目中有哪些分组,需要在如下的配置中指定:
//支持的分组列表'app_group_list': ['Home', 'Admin'], //表示有Home和Admin 2个分组
默认是哪个分组,可以在下面的配置中指定:
//默认分组'default_group': 'Home', //你可以将默认分组改成合适的,如:Blog
CBD模式
thinkjs使用CBD(核心Core+行为Behavior+驱动Driver)的架构模式,核心保留了最关键的部分,并在重要位置添加了切面,其他功能都是以驱动的方式来完成。
核心(Core)
thinkjs的核心部分包含通用函数库、系统默认配置、核心类库等组成,这些都是thinkjs必不可少的部分。
lib/Common/common.js 通用函数库lib/Common/extend.js js原生对象的扩展lib/Common/function.js 框架相关的函数库lib/Conf/alias.js 系统类库别名,加载时使用lib/Conf/config.js 系统默认配置lib/Conf/debug.js debug模式下的配置lib/Conf/mode.js 不同模式下的配置lib/Conf/tag.js 每个切面下的行为lib/Lib/Core/App.js 应用核心库lib/Lib/Core/Controller.js 控制器基类lib/Lib/Core/Db.js 数据库基类lib/Lib/Core/Dispatcher.js 路由分发类lib/Lib/Core/Http.js 封装的http对象类lib/Lib/Core/Model.js 模型基类lib/Lib/Core/Think.js 框架类lib/Lib/Core/View.js 视图类lib/Lib/Util/Behavior.js 行为基类lib/Lib/Util/Cache.js 缓存基类lib/Lib/Util/Cookie.js cookie类lib/Lib/Util/Filter.js 数据过滤类lib/Lib/Util/Session.js session基类lib/Lib/Util/Valid.js 验证类
行为(Behavior)
行为是thinkjs扩展机制中一项比较关键的扩展,行为可以独立调用,也可以整合到标签(tag)里一起调用,行为是执行过程中一个动作或事件。如:路由检测是个行为、静态缓存检测也是个行为。
标签(tag)是一组行为的集合,是在系统执行过程中切面处调用的。与EventEmitter不同,标签里的行为是按顺序执行的,当前的行为通过Promise机制控制后面的行为是否被执行。
系统标签位
当执行一个http请求时,会在对应的时机执行如下的标签位:
app_init应用初始化path_info解析path路径resource_check静态资源请求检测route_check路由检测app_begin应用开始action_initaction初始化view_init视图初始化view_template模版定位view_parse模版解析view_filter模版内容过滤view_end视图结束action_endaction结束app_end应用结束
在每一个标签位置都可以配置多个行为,系统的标签位行为如下:
/*** 系统标签配置* 可以在App/Conf/tag.js里进行修改* @type {Object}*/module.exports = {//应用初始化app_init: [],//pathinfo解析path_info: [],//静态资源请求检测resource_check: ['CheckResource'],//路由检测route_check: ['CheckRoute'],//应用开始app_begin: ['ReadHtmlCache'],//action执行初始化action_init: [],//模版解析初始化view_init: [],//定位模版文件view_template: ["LocationTemplate"],//模版解析view_parse: ["ParseTemplate"],//模版内容过滤view_filter: [],//模版解析结束view_end: ['WriteHtmlCache'],//action结束action_end: [],//应用结束app_end: []};
除了系统的标签位行为,开发人员也可以根据项目的需要自定义标签位行为。
自定义标签位行为文件在 App/Conf/tag.js。
行为定义
行为定义有2种方式,一种是一个简单的function,一种是较为复杂些的行为类。
可以通过下面直接function的方式创建一个简单的行为,文件为App/Conf/tag.js:
module.exports = {app_begin: [function(http){ //会传递一个包装的http对象if (http.group !== 'Home') {return;};var userAgent = http.getHeader('user-agent').toLowerCase();var flag = ["iphone", "android"].some(function(item){return userAgent.indexOf(item) > -1;})if (flag) {http.group = "Mobile";}}]}
该行为的作用是:如果当前的分组是Home并且是手机访问,那么将分组改为Mobile。这样就可以对同一个url,PC和Mobile访问执行不同的逻辑,输出不同的内容。
也可以继承行为基类来实现:
module.exports = Behavior(function(){return {run: function(){var http = this.http; //基类中的init方法会自动把传递进来的http对象放在当前对象上。if (http.group !== 'Home') {return;};var userAgent = http.getHeader('user-agent').toLowerCase();var flag = ["iphone", "android"].some(function(item){return userAgent.indexOf(item) > -1;})if (flag) {http.group = "Mobile";}}}})
将内容保存在App/Lib/Behavior/AgentBehavior.js文件中,并在App/Conf/tag.js中配置如下的内容:
module.exports = {app_begin: ["Agent"]}
使用哪种方式来创建行为可以根据行为里的逻辑复杂度来选择。
行为执行顺序
默认情况下,自定义的行为会和系统的行为一起执行,并且自定义行为是追加到系统行为之后的。
如果想更改行为执行的顺序,可以通过下面的方式:
app_begin: [true, 'Agent']将数组的第一个值设置为true, 表示自定义行为替换系统默认的行为,那么系统的默认行为则不在执行。app_begin: [false, 'Agent']将数组的第一个值设置为false, 表示自定义行为放在系统的默认行为之前执行。
自定义标签位执行和行为执行
上面提到的标签位和行为会在http执行过程中自动被调用,同时标签和行为也可以手工调用:
//执行check_auto标签位,//http为包装的http对象,在整个http执行过程中都可以获取到//data为传过去的数据, 如果行为里需要的是多个数据,那么这里应该传递个数组tag("check_auth", http, data);//执行Agent这个行为B("Agent", http, data);
驱动(Driver)
除了核心和行为外,thinkjs里的很多功能都是通过驱动来实现的,如:Cache, Session, Db等。
驱动包括:
lib/Lib/Driver/Cache缓存驱动lib/Lib/Driver/Db数据库驱动lib/Lib/Driver/SessionSession驱动lib/Lib/Driver/SocketSocket驱动lib/Lib/Driver/Template模版引擎驱动
如果有些功能框架里还没实现,如:mssql数据库,那么开发人员可以在项目里 App/Lib/Driver/Db/ 里实现。
自动加载
Node.js里虽然提供了require来加载模块,但对于应用内的文件加载并没有给出快捷的加载方式。为此thinkjs实现了一套快速加载机制,这些快速加载的文件包含:
- 系统核心文件
- 行为文件
- 各种驱动文件
通过全局函数thinkRequire来调用。例如:
//调用这些文件时会自动到对应的一些目录下查找var db = thinkRequire("mssqlDb");var model = thinkRequire("userModel");var behavior = thinkRequire("AgentBehavior");
这些文件具体包含:xxxBehavior, xxxModel, xxxController, xxxCache, xxxDb, xxxTemplate, xxxSocket, xxxSession
如果是这些之外的文件通过thinkRequire加载,那么会调用系统require函数。
系统流程
系统的执行流程分为启动服务和响应用户请求2块:
启动服务
- 通过
node index.js启动 - 调用系统入口文件
think.js - 常量定义,获取thinkjs的版本号
- 加载
lib/Lib/Core/Think.js文件,调用start方法 - 加载系统的函数库、系统默认配置
- 捕获异常
- 加载项目函数库、配置文件、自定义路由配置、行为配置、额外的配置文件
- 合并autoload的查找路径列表,注册autoload机制
- 记录当前Node.js的进程id
- 加载
lib/Lib/Core/App.js文件,调用run方法 - 识别是否使用cluster,开启http服务
响应用户请求
- 用户发生了url访问
- 执行标签位
form_parse - 发送
X-Powered-By响应头信息,值为thinkjs的版本号 - 执行标签位
app_init - 执行标签位
resource_check,判断当前请求是否是静态资源类请求 - 执行标签位
path_info,获取修改后的pathname - 执行标签位
route_check,进行路由检测,识别对应的Group, Controller, Action - 执行标签位
app_begin,检测当前请求是否有静态化缓存 - 执行标签位
action_init,实例化Controller - 调用
__before方法,如果存在的话 - 调用对应的action方法
- 调用
__after方法,如果存在的话 - 执行标签位
view_init,初始化模版引擎 - 执行标签位
view_template, 查到模版文件的具体路径 - 执行标签位
view_parse,解析模版内容 - 执行标签位
view_filter,对解析后的内容进行过滤 - 执行标签位
view_end,模版渲染结束 - 执行标签位
app_end, 应用调用结束
