资讯专栏INFORMATION COLUMN

Vue+thinkJs博客网站(二)之thinkJs的使用

desdik / 1940人阅读

摘要:插件机制可以说是自定义的钩子函数。首先在新建文件夹,新建文件以为例更新内容缓存里对应的数组表示钩子函数的调用名,具体调用的是下面的方法。

一.简介

这个个人博客网站最初制作的目的就是练习使用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部署相关可以看这里

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/102092.html

相关文章

  • Vue+thinkJs博客网站thinkJs使用

    摘要:插件机制可以说是自定义的钩子函数。首先在新建文件夹,新建文件以为例更新内容缓存里对应的数组表示钩子函数的调用名,具体调用的是下面的方法。 一.简介 这个个人博客网站最初制作的目的就是练习使用thinkJs,这一篇就主要讲一下thinkJs的一些特性和注意事项。涉及到了文件上传,thinkJs的插件机制,model层建立以及CURD的编写方式等。本项目github地址在这里。 项目thi...

    lucas 评论0 收藏0
  • Vue+thinkJs博客网站thinkJs使用

    摘要:插件机制可以说是自定义的钩子函数。首先在新建文件夹,新建文件以为例更新内容缓存里对应的数组表示钩子函数的调用名,具体调用的是下面的方法。 一.简介 这个个人博客网站最初制作的目的就是练习使用thinkJs,这一篇就主要讲一下thinkJs的一些特性和注意事项。涉及到了文件上传,thinkJs的插件机制,model层建立以及CURD的编写方式等。本项目github地址在这里。 项目thi...

    _Suqin 评论0 收藏0
  • Vue+thinkJs博客网站(一)vue多页面应用webpack3配置

    摘要:首先看下的代码编译前删除之前编译生成的静态资源首先需要改的是入口文件,因为是多页面应用,需要多个入口文件来保证打包成不同的。 一.项目简介     本项目使用vue作为前端框架,thinkJs作为后端框架,构建个人博客网站,页面分为博客展示和后台管理,主要目的是学习使用thinkJs。现在只完成了主要的博客增删改功能,发现webpack的配置遇到了一些坑,这里先记录下。项目目录结构如下...

    NickZhou 评论0 收藏0
  • Vue+thinkJs博客网站(一)vue多页面应用webpack3配置

    摘要:首先看下的代码编译前删除之前编译生成的静态资源首先需要改的是入口文件,因为是多页面应用,需要多个入口文件来保证打包成不同的。 一.项目简介     本项目使用vue作为前端框架,thinkJs作为后端框架,构建个人博客网站,页面分为博客展示和后台管理,主要目的是学习使用thinkJs。现在只完成了主要的博客增删改功能,发现webpack的配置遇到了一些坑,这里先记录下。项目目录结构如下...

    dongxiawu 评论0 收藏0
  • 使用 vue + thinkjs 开发博客程序记录

    摘要:一入冬懒癌发作,给自己找点事干。之前博客程序写过几次,的写过两次,用写过,随着版本从升级到之前的博客程序也做过升级。这里主要记录一下开发过程中遇到的问题和解决方法。后端使用守护进程即可。 一入冬懒癌发作,给自己找点事干。之前博客程序写过几次,php 的写过两次,nodejs 用 ThinkJS 写过,随着 ThinkJS 版本从1.x 升级到 2.x 之前的博客程序也做过升级。但是因为...

    Mike617 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<