1.简介本个人博客站的目的本来是为了练习使用thinkJs。本文主要讲一下thinkJs的一些特点和注意事项。涉及文件上传、thinkJs插件机制、模型层建立和CURD编写方法等,本项目github地址在这里。thinkJs这边的项目主要参考了知乎大佬Ischo的文章,链接在这里。2.ThinkJs模型层写法这里主要有两部分,一是表格对应的js文件,二是CRUD写法。项目表结构比较简单,一共有八张表,包括多对一、一对多、多对多关系。主要的表对应model文件夹下的js文件,表关系也是在这个js中维护的。这里我们以model/content.js为例子讲一哈:module.exports=classextendsthink.Model{//模型关联getrelation(){return{category:{type:think.Model.BELONG_TO,model:"meta",key:"category_id",fKey:"id",字段:"id,name,slug,description,count"},tag:{type:think.Model.MANY_TO_MANY,model:"meta",rModel:"关系",rfKey:"meta_id",key:"id",fKey:"content_id",字段:"id,name,slug,description,count"},comment:{type:think.Model.HAS_MANY,key:"id",fKey:"content_id",其中:"status=99",order:"create_timedesc"},user:{type:think.Model.BELONG_TO,model:"user",key:"user_id",fKey:"id",field:"id,username,email,qq,github,weibo,zhihu"}};}//添加文章asyncinsert(data){consttags=data.tag;data=this.parseContent(data);删除数据标签;constid=awaitthis.add(data);const关系=[];tags.forEach(val=>{relation.push({content_id:id,meta_id:val});});think.model("关系").addMany(关系);//更新文章数量this.updateCount(data.category_id,tags);返回ID;}}这里的代码没有截断,完整代码看github可以看到这个对象分为两部分,一是getrelation写的表映射关系。可以看到content表和meta表(类型:think.Model.BELONG_TO)是一对一的关系,其中key:category_id是content表中的字段,也就是外键,而fkey:id就是meta表中对应的字段。查询时会封装层user.category对象,对象属性为字段定义的id、name、slug、description、count。content和user也是多对多的关系(类型:think.Model.MANY_TO_MANY),rfModel是多对多关系下对应关系模型的名字,默认值是两者的组合modelnames,rfKey是多对多关系接下来是关系表对应的key。另一种是Model中的方法,相当于自定义模型方法。比如这里定义的insert可以通过this.model('content').insert()在controller中调用。thinkJS的CRUD操作并没有直接写sql,而是在sql的基础上封装了一层,通过调用model的方法来操作。think.Model基类提供了丰富的CRUD操作方法,如下:查询数据模型提供了多种查询数据的方法,如:find查询单条数据select查询多条数据count查询总数count选择页查询数据max查询字段的最大值avg查询字段的平均值min查询字段的最小值sum对语句中conditionlimit/page指定limitfield/fieldReverse的字段值求和SQL语句中指定fieldorder在SQL语句中指定ordergroup在SQL语句中指定groupjoin在SQL语句中指定joinunion在SQL语句中指定unionhaving在SQL语句中指定havingcache设置查询缓存添加数据模型提供了以下方法添加数据:add添加单条数据thenAddwhereconditiondoesnotexistaddaddManyaddmultipledataselectAddaddsubqueryresultdataupdate数据模型提供了以下几种更新数据的方法:update更新单条数据updateMany更新多条数据thenUpdate有条件的updateincrementfieldincrementvaluedecrementfielddecreasevaluedelete数据模型提供了以下几种删除数据的方法:delete删除数据手动执行SQL语句有时模型封装的方法不能满足所有情况,此时需要手动指定SQL语句,可以通过以下方式完成methods:query手写SQL语句queryexecute手写SQL语句执行比如我们要查询content表数据,在Controller中传入thin.model('content')。where(param).select()查询。thinkJs的Model层类似于之前使用的java数据层框架hibernate,基于面向对象的思想对sql、表和Model(实体类)进行封装,通过model方法进行CRUD操作,特别是保存sql。3.插件机制的实现参考博主实现的插件机制还是很有用的,就拿来这里吧。插件机制可以说是自定义的钩子函数。先在src新建service文件夹,新建js文件(以cache.js为例)module.exports=classextendsthink.Service{staticregisterHook(){return{content:["contentCreate","contentUpdate","contentDelete"]};}/***更新内容缓存*@param{[type]}data[description]*@return{[type]}[description]*/content(data){think.cache("recent_content",null);}};registerHook中content对应的数组表示钩子函数的调用名称,具体调用就是下面的content方法。像这样在控制器中调用awaitthis.hook("contentUpdate",data);hook函数的注册放在worker进程这里,thinkJs的具体运行过程可以到官网看这里。work.js的代码如下:think.beforeStartServer(async()=>{consthooks=[];for(constServiceofObject.values(think.app.services)){constisHookService=think.isFunction(Service.registerHook);if(!isHookService){continue;}constservice=newService();constserviceHooks=Service.registerHook();for(consthookFuncNameinserviceHooks){if(!think.isFunction(service[hookFuncName])){continue;}letfuncForHooks=serviceHooks[hookFuncName];if(think.isString(funcForHooks)){funcForHooks=[funcForHooks];}if(!think.isArray(funcForHooks)){continue;}for(consthookNameoffuncForHooks){如果(!hooks[hookName]){钩子[钩子名称]=[];}hooks[hookName].push({服务,方法:hookFuncName});}}}think.config("hooks",hooks);});这里遍历service中定义的方法取出来,按照一定的格式保存并存入数组,最后放到think.config中。项目启动后,这些流程已经执行完毕。think.Controller本身没有钩子方法。这里需要在extend中添加controller.js。代码如下:module.exports={/***Executionhook*@param{[type]}name[description]*@param{...[type]}args[description]*@return{[type]}[描述]*/asynchook(name,...args){const{hooks}=think.config();consthookFuncs=hooks[名称];如果(!think.isArray(hookFuncs)){返回;}for(const{service,method}ofhookFuncs){awaitservice[method](...args);}}};这样就实现了自定义的hook功能,一些常用的post方法可以直接分享一个。4.路由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规范。具体路由的详细解释可以参考官网链接5.部署项目的在线部署使用PM2来管理节点进程。部署的时候,src、view、www、pm2.json、production.js放到服务器上。安装好pm2后,运行pm2startpm2.json注意,在pm2.json中,需要将cwd修改为你的项目在服务器上的目录。本项目前后端是服务,不存在,所以没有nginx代理。ThinkJs部署可以看这里
