ThinksJS3.0

[TOC]

Command

安裝:
npm install -g think-cli

生成:

thinkjs new helloword
cd helloword
npm install
npm start

目錄結(jié)構(gòu)

|--- development.js   //開(kāi)發(fā)環(huán)境下的入口文件
|--- nginx.conf  //nginx 配置文件
|--- package.json
|--- pm2.json //pm2 配置文件
|--- production.js //生產(chǎn)環(huán)境下的入口文件
|--- README.md
|--- src
| |--- bootstrap  //啟動(dòng)自動(dòng)執(zhí)行目錄
| | |--- master.js //Master 進(jìn)程下自動(dòng)執(zhí)行
| | |--- worker.js //Worker 進(jìn)程下自動(dòng)執(zhí)行
| |--- config  //配置文件目錄
| | |--- adapter.js  // adapter 配置文件
| | |--- config.js  // 默認(rèn)配置文件
| | |--- config.production.js  //生產(chǎn)環(huán)境下的默認(rèn)配置文件病往,和 config.js 合并
| | |--- extend.js  //extend 配置文件
| | |--- middleware.js //middleware 配置文件
| | |--- router.js //自定義路由配置文件
| |--- controller  //控制器目錄
| | |--- base.js
| | |--- index.js
| |--- logic //logic 目錄
| | |--- index.js
| |--- model //模型目錄
| | |--- index.js
|--- view  //模板目錄
| |--- index_index.html
|--- www
| |--- static  //靜態(tài)資源目錄
| | |--- css
| | |--- img
| | |--- js

項(xiàng)目啟動(dòng)

系統(tǒng)服務(wù)啟動(dòng)

  1. 執(zhí)行 npm start 或者 node development.js壹店;
  2. 實(shí)例化 ThinkJS 里的 Application 類,執(zhí)行 run 方法鳖昌;
  3. 根據(jù)不同的環(huán)境(Master 進(jìn)程备畦、Worker 進(jìn)程、命令行調(diào)用)處理不同的邏輯遗遵;
    如果是 Master 進(jìn)程:
  • 加載配置文件萍恕,生成 think.configthink.logger 對(duì)象
  • 加載 src/bootstrap/master.js 文件
  • 如果配置文件監(jiān)聽(tīng)服務(wù),那么開(kāi)始監(jiān)聽(tīng)文件的變化车要,目錄為 src/
  • 文件修改后允粤,如果配置文件編譯服務(wù),那么會(huì)對(duì)文件進(jìn)行編譯翼岁,編譯到 app/ 目錄下
  • 根據(jù)配置 workers 來(lái) fork 對(duì)應(yīng)數(shù)量的 Worker类垫。Worker 進(jìn)程啟動(dòng)完成后,觸發(fā) appReady 事件(根據(jù) think.app.on("appReady") 來(lái)捕獲)
  • 如果文件發(fā)生了新的修改琅坡,會(huì)觸發(fā)編譯悉患,殺掉所有 Worker 進(jìn)程并重新 fork

如果是 Worker 進(jìn)程:

  • 加載配置文件,生成think.configthink.logger 對(duì)象
  • 加載 Extend榆俺,為框架提供更多的功能售躁,配置文件為 src/config/extend.js
  • 獲取當(dāng)前項(xiàng)目的模塊列表,放在 think.app.modules 上茴晋,如果為單模塊陪捷,那么值為空數(shù)組
  • 加載項(xiàng)目里的 controller 文件(src/controller/*.js),放在 think.app.controllers 對(duì)象上
  • 加載項(xiàng)目里的 logic 文件(src/logic/*.js)诺擅,放在 think.app.logics 對(duì)象上
  • 加載項(xiàng)目里的 model 文件(src/model/*.js)市袖,放在 think.app.models 對(duì)象上
  • 加載項(xiàng)目里的 service 文件(src/service/*.js),放在 think.app.services 對(duì)象上
  • 加載路由配置文件 src/config/router.js烁涌,放在 think.app.routers 對(duì)象上
  • 加載校驗(yàn)配置文件 src/config/validator.js苍碟,放在 think.app.validators 對(duì)象上
  • 加載 middleware 配置文件 src/config/middleware.js,并通過(guò) think.app.use 方法注冊(cè)
  • 加載定時(shí)任務(wù)配置文件 src/config/crontab.js撮执,并注冊(cè)定時(shí)任務(wù)服務(wù)
  • 加載 src/bootstrap/worker.js 啟動(dòng)文件
  • 監(jiān)聽(tīng) process 里的 onUncaughtExceptiononUnhandledRejection 錯(cuò)誤事件微峰,并進(jìn)行處理∈闱可以在配置 src/config.js 自定義這二個(gè)錯(cuò)誤的處理函數(shù)
  • 等待 think.beforeStartServer 注冊(cè)的啟動(dòng)前處理函數(shù)執(zhí)行县忌,這里可以注冊(cè)一些服務(wù)啟動(dòng)前的事務(wù)處理
  • 如果自定義了創(chuàng)建服務(wù)配置 createServer,那么執(zhí)行這個(gè)函數(shù) createServer(port, host, callback) 來(lái)創(chuàng)建服務(wù)
  • 如果沒(méi)有自定義继效,則通過(guò) think.app.listen 來(lái)啟動(dòng)服務(wù)
  • 服務(wù)啟動(dòng)完成時(shí)症杏,觸發(fā) appReady 事件,其他地方可以通過(guò) think.app.on("appReady") 監(jiān)聽(tīng)
  • 創(chuàng)建的服務(wù)賦值給 think.app.server 對(duì)象

用戶請(qǐng)求處理

  1. 請(qǐng)求到達(dá) WebServer瑞信,通過(guò)反向代理將請(qǐng)求轉(zhuǎn)發(fā)給 Node 服務(wù)
  2. Master 服務(wù)接收用戶請(qǐng)求厉颤,轉(zhuǎn)發(fā)給對(duì)應(yīng) Worker 進(jìn)程
  3. Work 進(jìn)程通過(guò)注冊(cè)的 Middleware 處理用戶請(qǐng)求
  4. 當(dāng) Worker 報(bào)錯(cuò),觸發(fā) onUncaughtException 或者 onUnhandledRejection 事件凡简,或者 Worker 異常退出逼友,Master 捕獲到錯(cuò)誤精肃,重新 fork 一個(gè)新的 Worker 進(jìn)程,并殺掉當(dāng)前進(jìn)程

所有的請(qǐng)求都是通過(guò) middleware 來(lái)完成的帜乞,具體的項(xiàng)目中司抱,根據(jù)需求可以組裝更多 middleware

Middleware

src/config/middleware.js 管理中間件
[配置](##配置 Middleware)
框架擴(kuò)展 App 參數(shù):

module.exports = (options, app) => {
// app 為 think.app 對(duì)象
return (ctx, next) => {
  ...
}
}

項(xiàng)目中自定義中間件

有時(shí)候項(xiàng)目中根據(jù)一些特定需要添加中間件,那么可以放在 src/middleware 目錄下黎烈,然后就可以直接通過(guò)字符串的方式引用

module.exports = [
{
  handle: 'csrf',
  options: {}
}
]

引入外部中間件

require 即可

const csrf = require('csrf');
module.exports = [
...,
{
  handle: csrf,
  options: {}
},
...
]

設(shè)置數(shù)據(jù)到 GET/POST 數(shù)據(jù)中

在中間件里可以通過(guò) ctx.param习柠、ctx.post 等方法來(lái)獲取 query 參數(shù)或者表單提交上來(lái)的數(shù)據(jù),但有些中間件里希望設(shè)置一些參數(shù)值照棋、表單值以便在后續(xù)的 Logic资溃、Controller 中獲取,這時(shí)候可以通過(guò) ctx.param烈炭、ctx.post 設(shè)置:

// 設(shè)置參數(shù) name=value溶锭,后續(xù)在 Logic、Controller 中可以通過(guò) this.get('name') 獲取該值
// 如果原本已經(jīng)有該參數(shù)符隙,那么會(huì)覆蓋
ctx.param('name', 'value');

// 設(shè)置 post 值趴捅,后續(xù) Logic、Controller 中可以通過(guò) this.post('name2') 獲取該值
ctx.post('name2', 'value');

Meta

處理一些通用的信息霹疫,如:設(shè)置請(qǐng)求的超時(shí)時(shí)間拱绑、是否發(fā)送 ThinkJS 版本號(hào)、是否發(fā)送處理的時(shí)間等更米。

Resource

處理靜態(tài)資源請(qǐng)求欺栗,靜態(tài)資源都放在 www/static/ 下毫痕,如果命中當(dāng)前請(qǐng)求是個(gè)靜態(tài)資源征峦,那么這個(gè) middleware 處理完后提前結(jié)束,不再執(zhí)行后面的 middleware消请。

Trace

處理一些錯(cuò)誤信息栏笆,開(kāi)發(fā)環(huán)境下打印詳細(xì)的錯(cuò)誤信息,生產(chǎn)環(huán)境只是報(bào)一個(gè)通用的錯(cuò)誤臊泰。

Payload

處理用戶上傳的數(shù)據(jù)蛉加,包含:表單數(shù)據(jù)、文件等缸逃。解析完成后將數(shù)據(jù)放在 request.body 對(duì)象上针饥,方便后續(xù)讀取。

Router

解析路由需频,解析出請(qǐng)求處理對(duì)應(yīng)的 ControllerAction丁眼,放在 ctx.controllerctx.action 上,方便后續(xù)處理昭殉。如果項(xiàng)目是多模塊結(jié)構(gòu)苞七,那么還有 ctx.module藐守。

Logic

根據(jù)解析出來(lái)的 controlleraction,調(diào)用 logic 里對(duì)應(yīng)的方法蹂风。

  • 實(shí)例化 logic 類卢厂,并將 ctx 傳遞進(jìn)去。如果不存在則直接跳過(guò)
  • 執(zhí)行 __before 方法惠啄,如果返回 false 則不再執(zhí)行后續(xù)所有的邏輯(提前結(jié)束處理)
  • 如果 xxxAction 方法存在則執(zhí)行慎恒,結(jié)果返回 false 則不再執(zhí)行后續(xù)所有的邏輯
  • 如果 xxxAction 方法不存在,則試圖執(zhí)行 __call 方法
  • 執(zhí)行 __after 方法礁阁,如果返回 false 則不再執(zhí)行后續(xù)所有的邏輯
  • 通過(guò)方法返回 false 來(lái)阻斷后續(xù)邏輯的執(zhí)行

Controller

根據(jù)解析出來(lái)的 controlleraction巧号,調(diào)用 controller 里的對(duì)應(yīng)的方法。

  • 具體的調(diào)用策略和 logic 完全一致
  • 如果不存在姥闭,那么當(dāng)前請(qǐng)求返回 404
  • action 執(zhí)行完成時(shí)丹鸿,可以將結(jié)果放在 this.body 屬性上然后返回給用戶

配置

配置 Middleware

框架統(tǒng)一在 src/config/middleware.js 中配置中間件:

const path = require('path')
const isDev = think.env === 'development'

module.exports = [
{
  handle: 'meta', //中間件處理函數(shù) 內(nèi)置中間件不用手工 require 進(jìn)來(lái),直接通過(guò)字符串方式引用
  options: {
    logRequest: isDev,
    sendResponseTime: isDev,
  },
},
{
  handle: 'resource',
  enable: isDev, //是否開(kāi)啟中間件
  options: {
    root: path.join(think.ROOT_PATH, 'www'),
    publicPath: /^\/static|favicon\.ico)/,
  },
}
]

handle:中間件函數(shù)名
enable:是否開(kāi)啟
options:傳遞的參數(shù)對(duì)象
match:匹配特定規(guī)則后才執(zhí)行該中間件

  1. 路徑匹配
  2. 函數(shù)匹配
module.exports = [
{
  handle: 'xxx-middleware',
  match: '/resource' //請(qǐng)求的 URL 是 /resource 打頭時(shí)才生效這個(gè) middleware
}
]

module.exports = [
{
  handle: 'xxx-middleware',
  match: ctx => { // match 為一個(gè)函數(shù)棚品,將 ctx 傳遞給這個(gè)函數(shù)靠欢,如果返回結(jié)果為 true,則啟用該 middleware
    return true;
  }
}
]

Extend

擴(kuò)展配置文件路徑為 src/config/extend.js

const view = require('think-view')

module.exports = [
view  //make application support view
]

通過(guò) view 擴(kuò)展框架就支持渲染模板的功能铜跑,Controller 類上就有 assign门怪、display 等方法

Context

ContextKoa 中處理用戶請(qǐng)求中的一個(gè)對(duì)象,貫徹整個(gè)生命周期锅纺,一般在 middleware掷空、controller、logic 中使用囤锉,簡(jiǎn)稱 ctx

module.exports = options => {
// 調(diào)用時(shí) ctx 作為第一個(gè)參數(shù)傳遞進(jìn)來(lái)
return (ctx, next) => {
  ...
}
}

module.exports = class extends think.Controller {
indexAction() {
  // controller 中 ctx 作為類的屬性存在坦弟,屬性名為 ctx
  // controller 實(shí)例化時(shí)會(huì)自動(dòng)把 ctx 傳遞進(jìn)來(lái)
  const ip = this.ctx.ip;
}
}

ThinkJS 框架繼承該對(duì)象,并通過(guò) Extend 機(jī)制可以擴(kuò)展 ctx 對(duì)象

Logic

ThinkJS 在控制器前面增加了一層 Logic 官地,其實(shí)就是在前面增加一個(gè) Logic 中間件提前處理請(qǐng)求酿傍,把一些重復(fù)的操作放到 Logic 中(參數(shù)校驗(yàn)、權(quán)限判斷等)驱入。

Controller

基類 think.Controller 控制器繼承該基類

Action 執(zhí)行

Action 執(zhí)行通過(guò)中間件 think-controller 完成赤炒,通過(guò) ctx.action 值在 controller 中尋找 xxxAction 方法名并調(diào)用,并且調(diào)用相關(guān)魔術(shù)方法亏较,具體順序如下:

  • 實(shí)例化 Controller 類莺褒,傳入 ctx 對(duì)象
  • 如果 __before 存在則調(diào)用,如果返回值為 false雪情,則停止繼續(xù)執(zhí)行
  • 如果方法 xxxAction 存在則執(zhí)行遵岩,如果返回值為 false, 則停止繼續(xù)執(zhí)行
  • 如果方法 xxxAction 不存在但 __call 方法存在旺罢,則調(diào)用旷余,如果返回值為 false绢记,則停止繼續(xù)執(zhí)行
  • 如果方法 __after 存在則執(zhí)行

如果類想調(diào)用父級(jí)的 __before 方法,可以通過(guò) super.__before 完成:

module.exports = class extends Base {
async __before(){
  // 通過(guò) Promise.resolve 將返回值包裝為 Promise
  // 如果返回值確定為 Promise正卧,那么就不需要再包裝了
  return Promise.resolve(super.__before()).then(flag => {
    // 如果父級(jí)想阻止后續(xù)繼承執(zhí)行會(huì)返回 false蠢熄,這里判斷為 false 的話不再繼續(xù)執(zhí)行了。
    if(flag === false) return false;
    // 其他邏輯代碼
  })
}
}

CTX 對(duì)象

Controller 實(shí)例化時(shí)會(huì)傳入 ctx 對(duì)象炉旷,通過(guò) this.ctx 獲取該對(duì)象签孔,子類重寫(xiě) constructor 方法,需要調(diào)用父類 construction 方法窘行,傳入 ctx 參數(shù)

const Base = require('./base.js');
module.exports = class extends Base {
constructor(ctx){
  super(ctx); // 調(diào)用父級(jí)的 constructor 方法饥追,并把 ctx 傳遞進(jìn)去
  // 其他額外的操作
}
}

多級(jí)控制器

有時(shí)候項(xiàng)目比較復(fù)雜,文件較多罐盔,所以希望根據(jù)功能進(jìn)行一些劃分但绕。如:用戶端的功能放在一塊、管理端的功能放在一塊惶看。
這時(shí)可以借助多級(jí)控制器來(lái)完成這個(gè)功能捏顺,在 src/controller/ 目錄下創(chuàng)建 user/admin/ 目錄,然后用戶端的功能文件都放在 user/ 目錄下纬黎,管理端的功能文件都放在 admin/ 目錄下幅骄。訪問(wèn)時(shí)帶上對(duì)應(yīng)的目錄名,路由解析時(shí)會(huì)優(yōu)先匹配目錄下的控制器本今。
假如控制器下有 console 子目錄拆座,下有 user.js 文件,即:src/controller/console/user.js冠息,當(dāng)訪問(wèn)請(qǐng)求為 /console/user/login 時(shí)挪凑,會(huì)優(yōu)先解析出 Controllerconsole/userActionlogin铐达。

透?jìng)鲾?shù)據(jù)

由于用戶的請(qǐng)求處理經(jīng)過(guò)了中間件岖赋、Logic瓮孙、Controller 等多層的處理,有時(shí)候希望在這些環(huán)節(jié)中透?jìng)饕恍?shù)據(jù)选脊,這時(shí)候可以通過(guò) ctx.state.xxx 來(lái)完成杭抠。

// 中間件中設(shè)置 state
(ctx, next) => {
ctx.state.userInfo = {};
}

// Logic、Controller 中獲取 state
indexAction() {
const userInfo = this.ctx.state.userInfo;
}

透?jìng)鲾?shù)據(jù)時(shí)避免直接在 ctx 對(duì)象上添加屬性恳啥,這樣可能會(huì)覆蓋已有的屬性偏灿,引起一些奇怪的問(wèn)題。

View

模板的配置由原來(lái)的 src/common/config/view.js 遷移至 src/config/config.js 中钝的,配置方法和之前基本一致翁垂。
其中老版本的 preRender() 方法已經(jīng)廢棄铆遭,新方法名為 beforeRender()nunjucks 模板引擎的參數(shù)順序由原來(lái)的 preRender(nunjucks, env, config) 修改為 beforeRender(env, nunjucks, config)沿猜。 // 模板渲染預(yù)處理

assign

給模板賦值

//單條賦值
this.assign('title', 'thinkjs');

//多條賦值
this.assign({
title: 'thinkjs',
name: 'test'
});

//獲取之前賦過(guò)的值枚荣,如果不存在則為 undefined
const title = this.assign('title');

//獲取所有賦的值
const assignData = this.assign();

render

獲取渲染后的內(nèi)容,該方法為異步方法啼肩,需要通過(guò) async/await 處理

//根據(jù)當(dāng)前請(qǐng)求解析的 controller 和 action 自動(dòng)匹配模板文件
const content1 = await this.render();

//指定文件名
const content2 = await this.render('doc');
const content3 = await this.render('doc/detail');
const content4 = await this.render('doc_detail');

//不指定文件名但切換模板類型
const content5 = await this.render(undefined, 'ejs');

//指定文件名且切換模板類型
const content6 = await this.render('doc', 'ejs');

//切換模板類型橄妆,并配置額外的參數(shù)
//切換模板類型時(shí),需要在 adapter 配置里配置對(duì)應(yīng)的類型
const content7 = await this.render('doc', {
type: 'ejs',
xxx: 'yyy'
});

display

渲染并輸出內(nèi)容祈坠,該方法實(shí)際上調(diào)用了 render 方法害碾,然后將渲染后的內(nèi)容賦值到 ctx.body 屬性上,該方法為異步方法赦拘,需要 async/await 處理

//根據(jù)當(dāng)前請(qǐng)求解析的 controller 和 action 自動(dòng)匹配模板文件
await this.display();

//指定文件名
await this.display('doc');
await this.display('doc/detail');
await this.display('doc_detail');

//不指定文件名切換模板類型
await this.display(undefined, 'ejs');

//指定文件名且切換模板類型
await this.display('doc', 'ejs');

//切換模板類型慌随,并配置額外的參數(shù)
await this.display('doc', {
type: 'ejs',
xxx: 'yyy'
});

模板預(yù)處理

beforeRender(env, nunjuncks, config) 方法進(jìn)行預(yù)處理,常見(jiàn)的需求是增加 Filter
env.addFilter('utc', time => (new Date(time)).toUTCString());

默認(rèn)注入的參數(shù) Controller Config Ctx

Controller:當(dāng)前控制器實(shí)例躺同,在模板里可以直接調(diào)用控制器上的屬性和方法儒陨。
這里以 nunjucks 模板引擎舉例,如果是調(diào)用控制器里的方法笋籽,那么方法必須為一個(gè)同步方法蹦漠。
Config:所有的配置,在模板里可以直接通過(guò) config.xxx 來(lái)獲取配置车海,如果屬性不存在笛园,那么值為 undefined。
Ctx:當(dāng)前請(qǐng)求的 Context 對(duì)象侍芝,在模板里可以通過(guò)直接通過(guò) ctx.xxx 調(diào)用其屬性或者 ctx.yyy() 調(diào)用其方法研铆。
如果是調(diào)用其方法,那么方法必須為一個(gè)同步方法州叠。

Router

解析

/console/user/login controller=console/user action=login
解析后的 module棵红、controller、action 分別放在 ctx.module咧栗、ctx.controller逆甜、ctx.action

自定義路由規(guī)則

配置文件 src/config/router.js 路由規(guī)則為二維數(shù)組:

module.exports = [
[/libs\/(.*)/i, '/libs/:1', 'get'],
[/fonts\/(.*)/i, '/fonts/:1', 'get,post'],
];

每一條路由規(guī)則也為一個(gè)數(shù)組,數(shù)組里面的項(xiàng)分別對(duì)應(yīng)為:
match{String | RegExp} pathname 匹配規(guī)則致板,可以是字符串或者正則交煞,如果是字符串,會(huì)通過(guò)path-to-regexp模塊轉(zhuǎn)為正則
pathname:匹配后調(diào)用的 pathname斟或,后續(xù)根據(jù)這個(gè)路徑解析 controller素征、action
method:支持的請(qǐng)求類型,默認(rèn)為所有
options:額外的選項(xiàng)

獲取 mactch 匹配的值

字符串路由:['/user/:name', 'user/info/:name'],在controller里可以通過(guò) this.get("name") 獲取
正則路由:[\/user\/(\w+)/, 'user?name=:1'] :1獲取這個(gè)值

Redirect

有時(shí)候項(xiàng)目經(jīng)過(guò)多次重構(gòu)后御毅,URL 地址可能會(huì)發(fā)生一些變化根欧,為了兼容之前的 URL,一般需要把之前的 URL 跳轉(zhuǎn)到新的 URL 上端蛆。這里可以通過(guò)將 method 設(shè)置為 redirect 來(lái)完成:

module.exporst = [
['/usersettings', '/user/setting', 'redirect', {statusCode: 301}]
]

Debug

DEBUG=think-router npm start 在路由解析時(shí)打印相關(guān)調(diào)試信息
當(dāng)訪問(wèn)地址為 /usersettings 時(shí)會(huì)自動(dòng)跳轉(zhuǎn)到 /user/setting咽块,同時(shí)指定此次請(qǐng)求的 statusCode301

Adapter

https://github.com/thinkjs/think-awesome#adapters
Adapter 是解決一類功能的多種實(shí)現(xiàn)問(wèn)題欺税,這些實(shí)現(xiàn)提供一套統(tǒng)一的接口侈沪,類似設(shè)計(jì)模式里的工廠模式,傳入不同的參數(shù)返回不同的實(shí)現(xiàn)(支持多種數(shù)據(jù)庫(kù)晚凿,多種模板引擎)亭罪,方便在不同實(shí)現(xiàn)中切換,Adapter 一般配合 Extend 一起使用歼秽。

const nunjucks = require('think-view-nunjucks');
const ejs = require('think-view-ejs');
const path = require('path');

exports.view = {
type: 'nunjucks', // 默認(rèn)的模板引擎為 nunjucks
common: { //通用配置
  viewPath: path.join(think.ROOT_PATH, 'view'),
  sep: '_',
  extname: '.html'
},
nunjucks: { // nunjucks 的具體配置
  handle: nunjucks
},
ejs: { // ejs 的具體配置
  handle: ejs,
  viewPath: path.join(think.ROOT_PATH, 'view/ejs/'),
}
}

exports.cache = {
...
}
  • type 默認(rèn)使用 Adapter 的類型应役,具體調(diào)用時(shí)可以傳遞參數(shù)改寫(xiě)
  • common 配置通過(guò)的一些參數(shù),項(xiàng)目啟動(dòng)時(shí)會(huì)跟具體的 adapter 參數(shù)合并
  • nunjucks ejs 配置特定類型的 Adapter 參數(shù)燥筷,最終獲取到的參數(shù)是 common 參數(shù)與該參數(shù)進(jìn)行合并
  • handle 對(duì)應(yīng)類型的處理函數(shù)箩祥,一般為一個(gè)類

Adapter 配置解析

Adapter 配置存儲(chǔ)所以類型下的詳細(xì)配置,具體使用時(shí)需要對(duì)其解析肆氓,選擇對(duì)應(yīng)的一種進(jìn)行使用袍祖,通過(guò) think-helper 模塊中的 parseAdapterConfig 方法完成解析:

const helper = require('think-helper');
const viewConfig = think.config('view'); // 獲取 view adapter 的詳細(xì)配置

const nunjucks = helper.parseAdatperConfig(viewConfig); // 獲取 nunjucks 的配置,默認(rèn) type 為 nunjucks
/**
{
type: 'nunjucks',
handle: nunjucks,
viewPath: path.join(think.ROOT_PATH, 'view'),
sep: '_',
extname: '.html'
}
*/
const ejs = helper.parseAdatperConfig(viewConfig, 'ejs') // 獲取 ejs 的配置
/**
{
handle: ejs,
type: 'ejs',
viewPath: path.join(think.ROOT_PATH, 'view/ejs/'),
viewPath: path.join(think.ROOT_PATH, 'view'),
sep: '_',
extname: '.html'
}
*/

拿到配置后谢揪,調(diào)用對(duì)應(yīng)的 handle 蕉陋,傳入配置然后執(zhí)行。
配置解析并不需要使用者在項(xiàng)目中具體調(diào)用拨扶,一般都是在插件對(duì)應(yīng)的方法里已經(jīng)處理凳鬓。

Adapter 使用

Adapter 都是一類功能的不同實(shí)現(xiàn),一般是不能獨(dú)立使用的患民,而是配合對(duì)應(yīng)的擴(kuò)展一起使用缩举。如:view Adapter(think-view-nunjucks、think-view-ejs) 配合 think-view 擴(kuò)展進(jìn)行使用匹颤。
項(xiàng)目安裝 think-view 擴(kuò)展后仅孩,提供了對(duì)應(yīng)的方法來(lái)渲染模板,但渲染不同的模板需要的模板引擎有對(duì)應(yīng)的 Adapter 來(lái)實(shí)現(xiàn)惋嚎,也就是配置中的 handle 字段杠氢。

Extend

擴(kuò)展框架的功能

Model

基類 think.Model

阻止后續(xù)執(zhí)行

移除了 think.prevent 等阻止后續(xù)執(zhí)行的方法站刑,替換為在 __before另伍、xxxAction、__after 中返回 false 來(lái)阻止后續(xù)代碼繼續(xù)執(zhí)行。

錯(cuò)誤處理

2.x 創(chuàng)建項(xiàng)目時(shí)摆尝,會(huì)創(chuàng)建對(duì)應(yīng)的 error.js 文件用來(lái)處理錯(cuò)誤温艇。3.0 里改為使用中間件 think-trace 處理。

加入 token 服務(wù)

npm install jsonwebtoken --save
thinkjs service token

生成 token 和 驗(yàn)證 token
'use strict';
//引入jwt
let jwt = require('jsonwebtoken');
//讀取secret標(biāo)記碼
let secret = think.config("gotolion.secret");
//讀取token有效期
let expiresIn = think.config("gotolion.expiresIn");
export default class extends think.service.base {
  /**
   * @description 創(chuàng)建token
   * @param {Object} userinfo 用戶信息
   * @return 返回token
   */
  createToken(userinfo) {
      let result = jwt.sign(userinfo, secret);
      return result;
  }


  /**
   * @description 驗(yàn)證票據(jù)
   * @param {Object} token 用戶請(qǐng)求token
   * @return 返回 錯(cuò)誤或者解密過(guò)的token
   */
  verifyToken(token) {
      if (token) {
          try {
              let result = jwt.verify(token, secret);
              return result;
          } catch (err) {
              //票據(jù)驗(yàn)證失敗,需要提示需要重新登錄
              return "fail";
          }
      }
      return "fail";
  }

}

token中包含用戶的姓名堕汞、部門(mén)等等用戶基本信息或者程序所需要的信息勺爱,原則是前端每次登錄獲取token,每次請(qǐng)求后端都在header中帶上token讯检,服務(wù)端驗(yàn)證token的合法性琐鲁。

Logic

邏輯處理。每個(gè)操作執(zhí)行前可以先進(jìn)行邏輯校驗(yàn):參數(shù)是否合法人灼、提交的數(shù)據(jù)是否正常围段、當(dāng)前用戶是否已經(jīng)登錄、當(dāng)前用戶是否有權(quán)限等等投放,可以降低 controller 里的復(fù)雜性奈泪。

'use strict';
/**
* logic
* @param  {} []
* @return {}     []
*/
export default class extends think.logic.base {
/**
 * index action logic
 * @return {} []
 */
indexAction(){

}
}

WWW

項(xiàng)目的可訪問(wèn)根目錄,nginx 里的根目錄會(huì)配置到此目錄下灸芳。
www/development.js
開(kāi)發(fā)模式下項(xiàng)目的入口文件涝桅,可以根據(jù)項(xiàng)目需要進(jìn)行修改。www/production.js 為線上的入口文件烙样。
www/static
存放一些靜態(tài)資源文件冯遂。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市谒获,隨后出現(xiàn)的幾起案子债蜜,更是在濱河造成了極大的恐慌,老刑警劉巖究反,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寻定,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡精耐,警方通過(guò)查閱死者的電腦和手機(jī)狼速,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)卦停,“玉大人向胡,你說(shuō)我怎么就攤上這事【辏” “怎么了僵芹?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)小槐。 經(jīng)常有香客問(wèn)我拇派,道長(zhǎng)荷辕,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任件豌,我火速辦了婚禮疮方,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘茧彤。我一直安慰自己骡显,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布曾掂。 她就那樣靜靜地躺著惫谤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪珠洗。 梳的紋絲不亂的頭發(fā)上石挂,一...
    開(kāi)封第一講書(shū)人閱讀 51,370評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音险污,去河邊找鬼痹愚。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蛔糯,可吹牛的內(nèi)容都是我干的拯腮。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蚁飒,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼动壤!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起淮逻,我...
    開(kāi)封第一講書(shū)人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤琼懊,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后爬早,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體哼丈,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年筛严,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了醉旦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡桨啃,死狀恐怖车胡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情照瘾,我是刑警寧澤匈棘,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站析命,受9級(jí)特大地震影響主卫,放射性物質(zhì)發(fā)生泄漏逃默。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一队秩、第九天 我趴在偏房一處隱蔽的房頂上張望笑旺。 院中可真熱鬧昼浦,春花似錦馍资、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至使兔,卻和暖如春建钥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背虐沥。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工熊经, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人欲险。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓镐依,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親天试。 傳聞我的和親對(duì)象是個(gè)殘疾皇子槐壳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容