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

一.簡介html

這個我的博客網站最初製做的目的就是練習使用thinkJs,這一篇就主要講一下thinkJs的一些特性和注意事項。涉及到了文件上傳,thinkJs的插件機制,model層創建以及CURD的編寫方式等。本項目github地址在這裏。java

項目thinkJs端主要參考了知乎上大佬Ischo的文章,連接在這。node

二.thinkJs model層寫法nginx

這裏主要講兩個部分,一是表對應的js文件,二是CRUD寫法。項目表結構比較簡單,一共八個表,包含多對一,一對多,多對多關係。主要的幾個表,都對應着model文件夾下的js文件,表關係也在這個js裏維護。這裏咱們以model/content.js爲例講一哈:git

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。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。sql

另外一個是Model裏的方法,至關於自定義的model方法,好比這裏定義的insert,就能夠在controller裏經過this.model('content').insert()調用。json

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()來查詢。api

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部署相關能夠看這裏

相關文章
相關標籤/搜索