一.简介
这个个人博客网站最初制造的目标就是演习运用thinkJs,这一篇就重要讲一下thinkJs的一些特征和注重事项。触及到了文件上传,thinkJs的插件机制,model层竖立以及CURD的编写体式格局等。本项目github地点在这里。
项目thinkJs端重要参考了知乎上大佬Ischo的文章,链接在这。
二.thinkJs model层写法
这里重要讲两个部份,一是表对应的js文件,二是CRUD写法。项目表构造比较简单,一共八个表,包括多对一,一对多,多对多关联。重要的几个表,都对应着model文件夹下的js文件,表关联也在这个js里保护。这里我们以model/content.js为例讲一哈:
module.exports = class extends think.Model {
// 模子关联
get relation() {
return {
category: {
type: think.Model.BELONG_TO,
model: "meta",
key: "category_id",
fKey: "id",
field: "id,name,slug,description,count"
},
tag: {
type: think.Model.MANY_TO_MANY,
model: "meta",
rModel: "relationship",
rfKey: "meta_id",
key: "id",
fKey: "content_id",
field: "id,name,slug,description,count"
},
comment: {
type: think.Model.HAS_MANY,
key: "id",
fKey: "content_id",
where: "status=99",
order: "create_time desc"
},
user: {
type: think.Model.BELONG_TO,
model: "user",
key: "user_id",
fKey: "id",
field: "id,username,email,qq,github,weibo,zhihu"
}
};
}
// 增加文章
async insert(data) {
const tags = data.tag;
data = this.parseContent(data);
delete data.tag;
const id = await this.add(data);
const relation = [];
tags.forEach(val => {
relation.push({
content_id: id,
meta_id: val
});
});
think.model("relationship").addMany(relation);
// 更新文章数目
this.updateCount(data.category_id, tags);
return id;
}
}
这里代码没有截全,完全代码看github。
我们看到这个对象分为两部份,一个是get relation写的表映照关联。能够看到content表与meta表存在一对一关联(type: think.Model.BELONG_TO),这里key:category_id是content内外的字段,即外键,fkey:id是对应的meta内外的字段。查询时,会封装层user.category对象,对象属性就是field 定义的id,name,slug,description,count。content 与user也存在多对多关联(type: think.Model.MANY_TO_MANY),rfModel是多对多关联下,对应的关联关联模子名,默认值为二个模子名的组合,rfKey是多对多关联下,关联表对应的 key。
另一个是Model里的要领,相当于自定义的model要领,比方这里定义的insert,就能够在controller里经由历程this.model(‘content’).insert()挪用。
thinkJS的CRUD操纵,不是直接写sql,而是在sql基础上封装一层,经由历程挪用model的要领来操纵。think.Model 基类供应了雄厚的要领举行 CRUD 操纵,细致以下:
查询数据
模子供应了多种要领来查询数据,如:
find 查询单条数据
select 查询多条数据
count 查询总条数
countSelect 分页查询数据
max 查询字段的最大值
avg 查询字段的平均值
min 查询字段的最小值
sum 对字段值举行乞降
getField 查询指定字段的值
同时模子支撑经由历程下面的要领指定 SQL 语句中的特定前提,如:
where 指定 SQL 语句中的 where 前提
limit / page 指定 SQL 语句中的 limit
field / fieldReverse 指定 SQL 语句中的 field
order 指定 SQL 语句中的 order
group 指定 SQL 语句中的 group
join 指定 SQL 语句中的 join
union 指定 SQL 语句中的 union
having 指定 SQL 语句中的 having
cache 设置查询缓存
增加数据
模子供应了以下的要领来增加数据:
add 增加单条数据
thenAdd where 前提不存在时增加
addMany 增加多条数据
selectAdd 增加子查询的效果数据
更新数据
模子供应了以下的要领来更新数据:
update 更新单条数据
updateMany 更新多条数据
thenUpdate 前提式更新
increment 字段增加值
decrement 字段削减值
删除数据
模子供应了以下的要领来删除数据:
delete 删除数据
手动实行 SQL 语句
有时候模子包装的要领不能满足一切的状况,这时候须要手工指定 SQL 语句,能够经由历程下面的要领举行:
query 手写 SQL 语句查询
execute 手写 SQL 语句实行
比方我们要查询content表数据,在Controller里经由历程thin.model(‘content’).where(param).select()来查询。
thinkJs的Model层与之前用过的java的数据层框架hibernate比较类似,都是基于面向对象的头脑对sql举行封装,表与Model(实体类),经由历程model要领举行CRUD操纵,特别省sql。
三.插件机制的完成
参考的博主完成的插件机制照样很好用的,这里我就拿了过来。插件机制能够说是自定义的钩子函数。首先在src新建service文件夹,新建js文件(以cache.js为例)
module.exports = class extends think.Service {
static registerHook() {
return {
content: ["contentCreate", "contentUpdate", "contentDelete"]
};
}
/**
* 更新内容缓存
* @param {[type]} data [description]
* @return {[type]} [description]
*/
content(data) {
think.cache("recent_content", null);
}
};
registerHook里content对应的数组示意钩子函数的挪用名,细致挪用的是下面的content要领。在controller里这么挪用
await this.hook("contentUpdate", data);
钩子函数的注册这里放到了worker历程里,thinkJs运转流程细致的能够看看官网在这里
work.js代码以下:
think.beforeStartServer(async () => {
const hooks = [];
for (const Service of Object.values(think.app.services)) {
const isHookService = think.isFunction(Service.registerHook);
if (!isHookService) {
continue;
}
const service = new Service();
const serviceHooks = Service.registerHook();
for (const hookFuncName in serviceHooks) {
if (!think.isFunction(service[hookFuncName])) {
continue;
}
let funcForHooks = serviceHooks[hookFuncName];
if (think.isString(funcForHooks)) {
funcForHooks = [funcForHooks];
}
if (!think.isArray(funcForHooks)) {
continue;
}
for (const hookName of funcForHooks) {
if (!hooks[hookName]) {
hooks[hookName] = [];
}
hooks[hookName].push({ service, method: hookFuncName });
}
}
}
think.config("hooks", hooks);
});
这里将service里定义的method遍历掏出,按肯定花样保留并存放到数组,末了放到think.config内里,项目启动后这些历程就已实行了。
think.Controller自身没有hook要领,这里须要在extend内里加上controller.js,代码以下:
module.exports = {
/**
* 实行hook
* @param {[type]} name [description]
* @param {...[type]} args [description]
* @return {[type]} [description]
*/
async hook(name, ...args) {
const { hooks } = think.config();
const hookFuncs = hooks[name];
if (!think.isArray(hookFuncs)) {
return;
}
for (const { service, method } of hookFuncs) {
await service[method](...args);
}
}
};
如许自定义钩子函数就完成了,一些通用的后置要领就能够直接共用一个了。
四.路由
thinkJs路由写在config/router.js里,细致代码以下:
module.exports = [
// RESTFUL
[/\/api\/(\w+)(?:\/(.*))?/, 'api/:1?id=:2', 'rest'],
[/\/font\/(\w+)\/(\w+)/, 'fontend/:1/:2'],
['/:category/:slug', 'content/detail'],
['/:category/:slug/comment', 'content/comment']
];
内里的数组的第一个元素是婚配url的表达式,第二个元素是分派的资本,如果是采纳RESTFUL范例定义的接口,第三个元素要写作’rest’。本项目标背景接口基础都是采纳RESTFUL范例,细致路由的细致解说能够看官网链接
五.布置
项目线上布置采纳PM2治理node历程,布置时把src,view,www,pm2.json,production.js放到效劳器上。安装好pm2后运转
pm2 start pm2.json
注重pm2.json里须要修正cwd为效劳器上你项目标目次。本项现在后端是一个效劳,不存在所以没有用nginx代办。thinkJs布置相干能够看这里