think
是一个全局对象,该对象里包含了大量有用的属性和方法。这些方法在应用的任何地方都可以直接使用,无需再 require。
服务启动时间,是个 unix
时间戳。
当前项目运行的环境,默认支持下面 3 个值,可以在项目启动时指定:
development
开发环境,会自动更新修改的文件testing
测试环境production
线上环境,代码上线时使用项目的文件夹名称,可以在项目启动时指定,默认值如下:
think.dirname = {
config: 'config', //配置文件目录
controller: 'controller', //控制器目录
model: 'model', //模型目录
adapter: 'adapter', //适配器目录
logic: 'logic', //逻辑目录
service: 'service', //服务目录
view: 'view', //视图目录
middleware: 'middleware', //中间件目录
common: 'common', //通用目录
bootstrap: 'bootstrap', //启动目录
locale: 'locale' //本土化目录
}
项目运行的端口,可以在项目启动时指定。如果指定,则忽略配置文件里的端口。
目录分隔符,等同于 path.sep
。
是否是 master 进程。
是否是命令行模式在运行项目,默认为 false
。如果是命令行模式,则该值为传递的参数,可以通过下面的方式启动命令行模式。
node www/index.js /home/index/test
系统当前的语言,从环境变量中读取,在 Windows
下可能为空。
项目当前的模式,框架支持 2 中项目模式:
think.mode_normal
多模块模式,目录结构只有 Controller
,View
,Logic
等分模块think.mode_module
多模块模式,严格按照模块来划分目录结构ThinkJS当前的版本
当前项目下的模块列表。
ThinkJS代码的路径
ThinkJS代码 lib/
的具体路径
项目的根目录,在 www/index.js
中定义
项目的 app
目录,在 www/index.js
中定义
项目的静态资源根目录,在 www/index.js
中定义
Runtime 目录,默认为当前项目下的 runtime/
目录。
动态的创建一个类,默认继承自 think.base 。 如果使用 ES6 特性进行开发的话,可以直接使用 ES6 里的 class 来创建类。
//继承自 think.base
var Cls1 = think.Class({
getName: function(){
}
})
var Cls2 = think.Class({
getName: function(){
}
}, true);
//继承自 Cls2
var Cls3 = think.Class(Cls2, {
init: function(name){
this.name = name;
},
getName: function(){
}
})
//获取类的实例,自动调用 init 方法
var instance = new Cls3('thinkjs');
target
{Object} 目录对象source1
{Mixed} 源对象1return
{Object} 目录对象将 source1, source2 等对象上的属性或方法复制到 target 对象上,类似于 jQuery 里的 $.extend 方法。
默认为深度复制,可以将第一个参数传 false
进行浅度复制。
think.extend({}, {name: 'foo'}, {value: 'bar'});
// returns
{name: 'foo', value: 'bar'}
obj
{Mixed} 要检测的对象return
{Boolean}检测一个对象是否是布尔值。
think.isBoolean(true); //true
think.isBoolean(false); //true
think.isBoolean('string'); //false
obj
{Mixed} 要检测的对象return
{Boolean}检测一个对象是否是数字。
think.isNumber(1); //true
think.isNumber(1.21); //true
obj
{Mixed} 要检测的对象return
{Boolean}检测是否是对象
think.isObject({}); //true
think.isObject({name: "welefen"}); //true
think.isObject(new Buffer('welefen')); //false
obj
{Mixed} 要检测的对象return
{Boolean}检测是否是字符串
think.isString("xxx"); // true
think.isString(new String("xxx")); //true
obj
{Mixed} 要检测的对象return
{Boolean}检测是否是函数
think.isFunction(function(){}); //true
think.isFunction(new Function("")); //true
obj
{Mixed} 要检测的对象return
{Boolean}检测是否是日期对象
think.isDate(new Date()); //true
obj
{Mixed} 要检测的对象return
{Boolean}检测是否是正则
think.isRegExp(/\w+/); //true
think.isRegExp(new RegExp("/\\w+/")); //true
obj
{Mixed} 要检测的对象return
{Boolean}检测是否是个错误
think.isError(new Error("xxx")); //true
obj
{Mixed} 要检测的对象return
{Boolean}检测是否为空
// 检测是否为空
think.isEmpty({}); //true
think.isEmpty([]); //true
think.isEmpty(""); //true
think.isEmpty(0); //true
think.isEmpty(null); //true
think.isEmpty(undefined); //true
think.isEmpty(false); //true
obj
{Mixed} 要检测的对象return
{Boolean}检测是否是数组
think.isArray([]); //true
think.isArray([1, 2]); //true
think.isArray(new Array(10)); //true
obj
{Mixed} 要检测的对象return
{Boolean}检测是否是 IP4
think.isIP4("10.0.0.1"); //true
think.isIP4("192.168.1.1"); //true
obj
{Mixed} 要检测的对象return
{Boolean}检测是否是 IP6
think.isIP6("2031:0000:130f:0000:0000:09c0:876a:130b"); //true
think.isIP6("2031:0000:130f::09c0:876a:130b"); //true
obj
{Mixed} 要检测的对象return
{Boolean}检测是否是 IP
think.isIP("10.0.0.1"); //true
think.isIP("192.168.1.1"); //true
think.isIP("2031:0000:130f:0000:0000:09c0:876a:130b"); //true ip6
file
{Mixed} 要检测的文件路径return
{Boolean}检测是否是文件,如果在不存在则返回 false
think.isFile("/home/welefen/a.txt"); //true
think.isFile("/home/welefen/dirname"); //false
file
{Mixed} 要检测的文件路径return
{Boolean}异步检测是否是文件,返回一个 Promise。该方法在 2.1.5
版本中添加。
dir
{Mixed} 要检测的路径return
{Boolean}检测是否是目录,如果不存在则返回 false
think.isDir("/home/welefen/dirname"); //true
dir
{Mixed} 要检测的路径return
{Boolean}异步检测是否是目录,返回 Promise。该方法在 2.1.5
版本中添加。
date
{Date} return
{String}返回一个格式化的日期,格式为:YYYY-MM-DD HH:ii:ss
,如:
let str = think.datetime();
//str is 2016-02-01 10:00:00
该方法在 2.1.5
版本中添加。
obj
{Mixed} 要检测的对象return
{Boolean}检测是否是 Buffer
think.isBuffer(new Buffer(20)); //true
obj
{Mixed} 要检测的对象return
{Boolean}是否是字符串类型的数字
think.isNumberString(1); //true
think.isNumberString("1"); //true
think.isNumberString("1.23"); //true
obj
{Mixed} 要检测的对象return
{Boolean}检测是否是个 Promise
think.isPromise(new Promise(function(){})); //true
think.isPromise(getPromise()); //true
obj
{Mixed} 要检测的对象return
{Boolean}检测是否是包装的 http 对象
think.isHttp(http); // true
path
{String} 要写的目录return
{Boolean}判断文件或者目录是否可写,如果不存在则返回 false
obj
{Mixed}return
{Boolean}判断是否是个阻止类型的 Promise。通过 think.prevent() 会生成该 Promise 。
p
{String} 要创建的目录mode
{Number} 要创建的目录权限,默认为 0777
递归的创建目录,如果目录已经存在,那么修改目录的权限。
// 假设 /home/welefen/a/b/ 不存在
think.mkdir("/home/welefen/a/b");
think.mkdir("home/welefne/a/b/c/d/e"); // 递归创建子目录
p
{String} 要删除的目录reserve
{Boolean} 是否保留该目录。如果为 true,则只删除子目录return
{Promise}递归的删除目录,如果目录不存在则直接返回。返回是个 Promise,后续操作要在 then
里执行
function rmTmp(){
think.rmdir('/foo/bar').then(function(){
//后续其他操作
})
}
如果使用 Generator Function
,则可以使用 yield
function * rmTmp(){
yield think.rmdir('/foo/bar');
//后续其他操作
}
p
{String} 要修改的目录mode
{Number} 目录权限,默认为0777
修改目录权限,如果目录不存在则直接返回
think.chmod("/home/welefen/a", 0777);
str
{String} 要计算的字符串return
{String} 返回字符串的 md5 值计算字符串的 md5 值
think.md5('thinkjs');
// returns 7821eb623e0b1138a47db6a88c3f56bc
str
{String} 要转换的字符串return
{String}转换为驼峰方式
think.camelCase('a_bbb_ccc');
//returns aBbbCcc
return
{Object} Deferred对象创建一个 Deferred
对象,new Promise
的一种快捷方式。虽然不建议使用 Deferred
这种方式,但有时候不得不使用。如:setTimeout
, event
。
//使用Deferred的方式
var fn = function(){
var deferred = think.defer();
process.nextTick(function(){
if(xxx){
deferred.resolve(data);
}else{
deferred.reject(err);
}
})
return deferred.promise;
}
使用 Deferred
方式比直接使用 new Promise
的方法代码更加简洁。
//直接使用new Promise的方式
var fn = function(){
return new Promise(function(resolve, reject){
process.nextTick(function(){
if(xxx){
resolve(data);
}else{
reject(err);
}
})
})
}
注: 异步 callback
的操作不要使用 Deferred
方式,可以用 think.promisify
方法快速把 callback
包装成 Promise
。
fn
{Function} 要转化的函数receiver
{Object} this指向将异步方法快速包装成 Promise,异步方法必须符合最后一个参数为回调函数,且回调函数的第一个参数为 err
的原则。
var fs = require('fs');
//获取文件内容
var getContent = function(filePath){
//将readFile方法包装成Promise
var readFilePromise = think.promisify(fs.readFile, fs);
//读取文件内容
return readFilePromise(filePath, 'utf8');
}
//获取具体的文件内容
getContent('/foo/bar/file.txt').then(function(content){
console.log(content);
}).catch(function(err){
console.error(err.stack);
})
err
{Error} Error对象return
{Promise} reject promise返回一个 reject promise,与 Promise.reject
不同的是,该方法会自动打印错误信息。避免需要调用 catch 方法手工打印错误信息。
//使用Promise.reject
var fn = function(){
return Promise.reject(new Error('xxx'));
}
//需要手工调用catch方法打印错误信息
fn().catch(function(err){
console.error(err.stack);
})
//使用think.reject
var fn = function(){
return think.reject(new Error('xxx'));
}
//会自动打印格式化后的错误信息
fn();
co
模块的别名 https://github.com/tj/co
name
{String} 类名type
{String} 类型 (controller | model | logic ...)module
{String} 模块名base
{String} 找不到时找对应的基类根据类型,名称来查找类。如果找不到会到 common 模块下查找,如果还是找不到,则查找对应类型的基类。
//查找 home 模块下 user controller
//如果找不到,会找 common 模块下 user controller
//如果还是找不到,会找 base controller
think.lookClass('user', 'controller', 'home');
//查找 admin 模块下 user controller
think.lookClass('admin/user', 'controller');
module
{String} 模块名type
{String} 类型,如: controller, model, logicprefix
{String} 前缀根据当前项目类型获取对应类型的目录。
let path = think.getPath('home', 'controller');
假如当前项目的根目录是/foo/bar
,那么获取到的目录为:
think.mode_normal
下路径为 /foo/bar/app/controller/home
think.mode_module
下路径为 /foo/bar/app/home/controller
name
{String} flag
{Boolean}file
{String} 要加载的文件安全的加载一个文件,如果文件不存在,则返回 null,并打印错误信息。
返回一个特殊的 reject promise 。该 Promise 可以阻止后续的行为且不会报错。
msg
{String | Error} 信息type
{String} 类型showTime
{Number | Boolean} 是否显示时间打印日志,该方法打印出来的日志会有时间,类型等信息,方便查看和后续处理。
think.log('WebSocket Status: closed', 'THINK');
//writes '[2015-09-23 17:43:00] [THINK] WebSocket Status: closed'
think.log(new Error('error'), 'ERROR');
//writes '[2015-09-23 17:50:17] [Error] Error: error'
think.log('/static/module/jquery/1.9.1/jquery.js', 'HTTP', startTime);
//writes '[2015-09-23 17:52:13] [HTTP] /static/module/jquery/1.9.1/jquery.js 10ms'
think.log('/static/module/jquery/1.9.1/jquery.js', 'HTTP', null);
//writes '[HTTP] /static/module/jquery/1.9.1/jquery.js'
think.log(function(colors){
return colors.yellow('[WARNING]') + ' test';
});
//writes '[WARNING] test'
其中 colors
为 npm 模块 colors,https://github.com/Marak/colors.js 。
name
{String} 配置名称value
{Mixed} 配置值data
{Object} 配置对象读取或者设置配置,可以指定总的配置对象。
//获取配置
let value = think.config('name');
//获取 admin 模块下的配置
let value = think.config('name', undefined, 'admin');
// 写入配置
think.config('name', 'value');
module
{String} 模块名称return
{Object}获取模块的所有配置。该配置包含模块的配置,通用模块的配置,框架默认的配置。
//获取 admin 模块的所有配置
let configs = think.getModuleConfig('admin');
注册、获取和执行 hook,项目中可以根据需要追加或者修改。
think.hook('view_template');
//returns
['locate_template']
//替换原有的 hook
think.hook('view_template', ['locate_template1']);
//将原有的之前追加
think.hook('view_template', ['locate_template1'], 'prepend');
//将原有的之后追加
think.hook('view_template', ['locate_template1'], 'append');
think.hook('view_template', null);
let result = think.hook('view_template', http, data);
//result is a promise
注册、创建、获取和执行 middleware。
//解析 XML 示例
var ParseXML = think.middleware({
run: function(){
var http = this.http;
return http.getPayload().then(function(payload){
var data = xmlParse.parse(payload); //使用一个xml解析,这里 xmlParse 是示例
http._post = data; //将解析后的数据赋值给 http._post,后续可以通过 http.post('xxx') 获取
});
}
});
使用 ES6 创建 middleware。
let Cls1 = class extends think.middleware.base {
run(){
let http = this.http;
}
}
middleware 可以是个简单的 function,也可以是较为复杂的 class。
//注册 middleware 为 function
think.middleware('parse_xml', http => {
})
//注册 middleware 为 class
//会自动调用 run 执行
let Cls = think.middleware({
run: function(){
let http = this.http;
}
});
think.middleware('parse_xml', Cls);
let middleware = think.middleware('parse_xml');
let result = think.middleware('parse_xml', http);
//result is a promise
创建、注册、获取和执行 adapter。
//创建一个 adapter
var Cls = think.adapter({
});
//创建一个 session adapter,继承自 session base 类
var Cls = think.adapter('session', 'base', {
})
//使用 ES6 创建一个 session adapter
let Cls = class extends think.adapter.session {
}
//注册一个 xxx 类型的 session adapter
think.adapter('session', 'xxx', Cls);
//获取 file 类型的 session adapter
let Cls = think.adapter('session', 'file');
let Adapter = think.adapter('session', 'file');
let instance = new Adapter(options);
instance
{Object} 类的实例注册实例到 gc 队列中。instance 必须含有属性 gcType
和方法 gc
。
像 cache, session 这些功能一般都是有过期时间,过期后需要要进行清除工作。框架提供了一套机制方便清除过期的文件等。
let Cls = class extends think.adapter.cache {
init(options){
super.init(options);
this.gcType = 'xFileCache';
think.gc(this);
}
gc(){
//寻找过期的内容并清除
}
}
req
{Object} request 对象res
{Object} response 对象return
{Promise}根据 req 和 res 包装成 http 对象。req 和 res 可以自定义。
//根据一个 url 生成一个 http 对象,方便命令行下调用
think.http('/index/test').then(http => {
});
length
{Number} 生成字符串的长度,默认为 32生成一个随机字符串。
解析配置里的 adapter
和 parser
,如:
let config = think.parseConfig({prefix: 'think_'}, {
type: 'mysql',
adapter: {
mysql: {
prefix: 'test_',
host: ['10.0.0.1', '10.0.0.2'],
parser: options => {
return {
host: '10.0.0.1'
}
}
}
}
});
// config value is {prefix: 'test_', host: '10.0.0.1'}
如果只想解析 adapter
,而不解析 parser
,可以通过传递第一个参数为 true
,如:
let config = think.parseConfig(true, {prefix: 'think_'}, {
type: 'mysql',
adapter: {
mysql: {
prefix: 'test_',
host: ['10.0.0.1', '10.0.0.2'],
parser: options => {
return {
host: '10.0.0.1'
}
}
}
}
});
// config value is {prefix: 'test_', ['10.0.0.1', '10.0.0.2'], parser: function(){...}}
http
{Object} http对象生成 session,并写到 http 对象上。如果已经存在,则直接返回。
创建、执行 controller
//创建 controller, 继承 think.controller.base
let Cls = think.controller({
})
//创建 controller, 继承 think.controller.rest
let Cls = think.controller('rest', {
})
//使用 ES6 创建 controller
let Cls1 = class extends think.controller.base {
}
//实例化 home 模块下 user controller
let instance = think.controller('user', http, 'home');
创建、执行 logic
//创建 logic, 继承 think.logic.base
let Cls = think.logic({
})
//使用 ES6 创建 logic
let Cls1 = class extends think.logic.base {
}
//实例化 home 模块下 user logic
let instance = think.logic('user', http, 'home');
创建或者获取 model
//创建一个 model
let model = think.model({
getList: function(){
}
});
//ES6 里直接继承 think.model.base 类
let model = class extends think.model.base {
getList(){
}
}
//创建一个 model 继承自 mongo model
let model = think.model('mongo', {
getList: function(){
}
});
//ES6 里直接继承 think.model.mongo 类
let model = class extends think.model.mongo {
getList(){
}
}
let configs = {
host: '127.0.0.1',
name: 'user'
}
//获取 home 模块下 user model
let instance = think.model('user', configs, 'home');
创建或者获取 service
//创建一个 service 类
let service = think.service({
})
//ES6 里直接继承 think.service.base 类
let service = class extends think.service.base {
}
service 基类继承自 think.base,所以可以用 think.base 里的方法。
如果 serivce 不想写成类,那就没必要通过这种方法创建。
//获取 home 模块下 post service,并传递参数 {}
//如果获取到的 service 是个类,则自动实例化
think.service('post', {}, 'home');
name
{String} 缓存 keyvalue
{Mixed} 缓存值options
{Object} 缓存选项return
{Promise} 操作都是返回 Promise获取、设置或者删除缓存, value 是 undefined
表示读取缓存。 value 是 null
时删除缓存。
value 为 Function
时表示获取缓存,如果获取不到,则调用该函数,然后将返回值设置到缓存中并返回。
//获取缓存
think.cache('name').then(data => {});
//指定缓存类型获取,从 redis 里获取缓存
think.cache('name', undefined, {type: 'redis'});
//如果缓存 userList 不存在,则查询数据库,并将值设置到缓存中
think.cache('userList', () => {
return think.model('user').select();
});
//设置缓存
think.cache('name', 'value');
//删除缓存
think.cache('name', null);
key
{String} 要获取的 keydata
{Array} 参数根据语言获取对应的值,当前语言通过 think.lang
方法来获取,可以在系统启动时指定。
think.locale('CONTROLLER_NOT_FOUND', 'test', '/index/test');
//returns
'controller `test` not found. url is `/index/test`.'
注册、获取或执行检测。
//注册检测类型为 not_number
think.validate('not_number', value => {
return !(/^\d+$/.test(value));
})
let fn = think.validate('not_number');
let result = think.validate({
name: {
value: 'name',
required: true,
not_number: true
},
pwd: {
value: 'xxx',
required: true,
minLength: 6
}
});
//如果 result 是 isEmpty,表示数据都正常
if(think.isEmpty(result)){
}
key
{String} callback
{Function}执行等待,避免一个耗时的操作多次被执行。 callback 需要返回一个 Promise 。
如:用户访问时,要请求一个远程的接口数据。如果不处理,每个用户请求都会触发这个远程接口的访问,导致有很大的资源浪费。可以让这些用户公用一个远程接口的请求。
import superagent from 'superagent';
export default class extends think.controller.base {
* indexAction(){
let result = yield think.await('get_xxx_data', () => {
let req = superagent.post('xxxx');
let fn = think.promisify(req.end, req);
return fn();
});
this.success(result);
}
}
pkg
{String} 模块名加载模块。如果模块不存在,则自动安装。这样可以做到动态安装模块。
//如果mysql模块,则通过npm安装
let mysql = think.npm('mysql');
//指定版本加载一个模块
let mysql = think.npm('mysql@2.0.0')
err
{Error | Promise | String} 错误信息addon
{Error | String} 追加的错误信息格式化错误信息,将部分系统的错误信息描述完整化。
let error = think.error(new Error('xxx'));
let promise = Project.reject(new Error('xxx'));
promise = think.error(promise)
自动给 promise 追加 catch,捕获错误信息。
status
{Number} 状态码http
{Object} 包装的http对象log
{Boolean} 是否打印错误信息当系统出现异常时(系统错误,页面找不到,没权限等),显示对应的错误页面。
创建项目时,会在 common 模块下生成文件 src/common/controller/error.js
,专门用来处理错误情况。
默认支持的错误类型有:400
, 403
, 404
, 500
, 503
。
项目里可以根据需要修改错误页面或者扩展。
export default class extends think.controller.base {
indexAction(){
if(xxxx){
let error = new Error('not found');
//将错误信息写到 http 对象上,用于模版里显示
this.http.error = error;
return think.statusAction(404, this.http);
}
}
}
dataList
{Array} 要处理的数据列表callback
{Function} 处理函数,会将每条数据传递进去,需要返回 Promiseoptions
{Object} 额外选项return
{Promise}options
包含一下选项:
limit
{Number} 并发限制数,默认为 10 条ignoreError
{Boolean} 是否忽略错误,默认情况下一个错误后会停止后续执行并发限制处理方法。如:有 10000 条网络数据需要处理,如果同时处理会会网络 IO 错误,此时可以对并发处理进行限制。该方法在 2.0.6
版本中添加。
import superagent from 'superagent';
export default class extends think.controller.base {
async indexAction(){
let dataList = [...];
//result 为每条处理结果集合
//如果某些条数据处理异常,那么对应的数据为 undefined,处理时需要过滤下
let result = await think.parallelLimit(dataList, item => {
let url = item.url;
let req = superagent.get(url);
let fn = think.promisify(req.end, req); //将 end 方法包装成 Promise
return fn();
}, {
limit: 20, //一次执行 20 条
ignoreError: true
})
}
}
有些数据处理虽在一个情况下只用处理一次,但单次处理比较耗时,如果同时请求很多的话可能会导致报错。这个时候也要进行限制,如果当前同时处理数目较多,后续请求则进行等待。
这个需求可以通过传入一个相同的 key 将任务分组,如:
import gm from 'gm';
export default class extends think.controller.base {
async indexAction(){
let result = await think.parallelLimit('clip_image', () => {
let imageFile = this.file('image').path;
let instance = gm(imageFile).resize(240, 240).noProfile();
let fn = think.promisify(instance.write, instance);
return fn('/path/to/save/image.png');
}, {
limit: 20 //一次执行 20 条
})
}
}
think.base 详细介绍请见 这里
think.http.base 详细介绍请见 这里