[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)
- 執(zhí)行
npm start
或者node development.js
壹店; - 實(shí)例化 ThinkJS 里的 Application 類,執(zhí)行
run
方法鳖昌; - 根據(jù)不同的環(huán)境(Master 進(jìn)程备畦、Worker 進(jìn)程、命令行調(diào)用)處理不同的邏輯遗遵;
如果是 Master 進(jìn)程:
- 加載配置文件萍恕,生成
think.config
和think.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.config
和think.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 里的
onUncaughtException
和onUnhandledRejection
錯(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)求處理
- 請(qǐng)求到達(dá)
WebServer
瑞信,通過(guò)反向代理將請(qǐng)求轉(zhuǎn)發(fā)給Node
服務(wù) -
Master
服務(wù)接收用戶請(qǐng)求厉颤,轉(zhuǎn)發(fā)給對(duì)應(yīng) Worker 進(jìn)程 -
Work
進(jìn)程通過(guò)注冊(cè)的Middleware
處理用戶請(qǐng)求 - 當(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)的 Controller
和 Action
丁眼,放在 ctx.controller
和 ctx.action
上,方便后續(xù)處理昭殉。如果項(xiàng)目是多模塊結(jié)構(gòu)苞七,那么還有 ctx.module
藐守。
Logic
根據(jù)解析出來(lái)的 controller
和 action
,調(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)的 controller
和 action
巧号,調(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í)行該中間件
- 路徑匹配
- 函數(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
Context
是 Koa
中處理用戶請(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)先解析出 Controller
為 console/user
,Action
為 login
铐达。
透?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)求的 statusCode
為 301
。
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)資源文件冯遂。