前言
在node項(xiàng)目,不管是koa/express路由的使用中,我們創(chuàng)建路由一般都是這樣的姿勢(shì)
router.post("/api/test", middleware, handler); // 創(chuàng)建路由
比如:

這里小編推薦一個(gè)福利,更多精彩內(nèi)容請(qǐng)點(diǎn)擊鏈接,點(diǎn)擊這里
我們一般創(chuàng)建路由的handler會(huì)單獨(dú)抽到其它文件也就是所說(shuō)的controller,這樣就會(huì)多了一步編寫(xiě)router的過(guò)程。那么這一步是否可以省略呢?
當(dāng)然可以,本文帶你一步步使用裝飾器統(tǒng)一處理構(gòu)建路由,這樣不用在寫(xiě)完某一個(gè)controller的方法后再進(jìn)行創(chuàng)建router啦,使用裝飾器,我們只需要在某一個(gè)接口方法上添加路由的裝飾就可以進(jìn)行創(chuàng)建router。那么精彩來(lái)啦~
項(xiàng)目結(jié)構(gòu)

開(kāi)始啦,一步步教你使用裝飾器構(gòu)建路由嘍!
初始化項(xiàng)目
初始化package.json
新建一個(gè)目錄,用vscode打開(kāi)。執(zhí)行npm init初始化創(chuàng)建package.json
安裝koa項(xiàng)目使用的第三方包:
npm install -S koa koa-router koa-bodyparser koa-compress
- koa-router 管理路由
- koa-bodyparser 讀取post,put數(shù)據(jù)轉(zhuǎn)化對(duì)象格式
- koa-compress 壓縮請(qǐng)求數(shù)據(jù)提高傳輸速度
配置babel支持ES6 ES7
因?yàn)轫?xiàng)目使用裝飾器配置路由,必須支持ES7語(yǔ)法,才需要配置babel
本文配置的是babel7版本
下面我?guī)Т蠹乙徊讲脚渲胋abel
npm install -D @babel/core
npm install -D @babel/preset-env
npm install -D @babel/plugin-proposal-decorators
npm install -D @babel/register
babel7版本 使用的是@babel,7以下是babel-xxx 這點(diǎn)很容易區(qū)分
下面介紹一下這些包的功能
- @babel/core babel 核心代碼
- @babel/preset-env 編譯新版的語(yǔ)法 如:箭頭函數(shù),但是并不轉(zhuǎn)換新版api 如:Array.include 轉(zhuǎn)換新版api及兼容瀏覽器之間的差異(兼容ie)需要 babel-polyfill
- @babel/plugin-proposal-decorators 解析裝飾器
- @babel/register 在運(yùn)行時(shí)進(jìn)行即時(shí)編譯,不是進(jìn)行先編譯在運(yùn)行
然后在項(xiàng)目根目錄創(chuàng)建.babelrc文件
配置預(yù)設(shè),插件
{
"presets": [
["@babel/preset-env", {
"targets": {
"node": "current"
}
}]
],
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }]
]
}
到此,配置結(jié)束。項(xiàng)目比較簡(jiǎn)單,這些babel配置在本項(xiàng)目已經(jīng)足夠使用,接下來(lái)就可以愉快的使用es6,裝飾器新語(yǔ)法了
創(chuàng)建node服務(wù)
新建index.js,app.js啟動(dòng)一個(gè)服務(wù)
index.js 主要是進(jìn)行babel注冊(cè),以及啟動(dòng)服務(wù)的中介文件。
為什么會(huì)這樣呢?在這個(gè)文件進(jìn)行使用動(dòng)態(tài)編譯,但是編譯的時(shí)候是不會(huì)編譯index.js文件的,所以,這個(gè)文件還是需要使用e5的舊語(yǔ)法
require("@babel/register")
require("./app")
app.js才會(huì)去真正啟動(dòng)node服務(wù)
import Koa from "koa"
import bodyparser from "koa-bodyparser"
import compress from "koa-compress"
const app = new Koa();
app.use(compress());
app.use(bodyparser());
const PORT = 8081;
app.listen(PORT,()=>{
console.log(`啟動(dòng)成功! env=${process.env.NODE_ENV}`)
console.log(`Listening at http://localhost:${PORT}`)
})
配置運(yùn)行命令,啟動(dòng)服務(wù)
然后需要添加package.json的執(zhí)行命令
"dev":"cross-env NODE_ENV=development nodemon ./index.js"
這里用到了兩個(gè)node開(kāi)發(fā)中常用的兩個(gè)插件cross-env,nodemon其主要用途如下
安裝npm install -D cross-env nodemon,安裝到devDependencies
- cross-env 給項(xiàng)目添加環(huán)境變量區(qū)分是開(kāi)發(fā)環(huán)境和生產(chǎn)環(huán)境,一般在執(zhí)行npm命令前添加環(huán)境變量通過(guò)
這個(gè)地方添加了NODE_ENV變量,在程序中可以通過(guò)process.env.NODE_ENV獲取執(zhí)行命令添加的變量值
- nodemon 可以替代node啟動(dòng)服務(wù),只是nodemon功能豐富,可以監(jiān)聽(tīng)代碼的更改重新啟動(dòng)項(xiàng)目
下面執(zhí)行npm run dev可以看到服務(wù)啟動(dòng)成功

添加路由裝飾器
在項(xiàng)目根目錄新建common目錄,然后在common目錄下新建decorator目錄,在這個(gè)目錄新建index.js
no bi bi ,上代碼:
/**
* 請(qǐng)求方法
*/
export const RequestMethod = {
"GET":"get",
"POST": "post",
"PUT": "pust",
"DELETE": "delete",
"OPTION": "option",
"PATCH": "patch"
}
/**
* 定義注冊(cè)的路由數(shù)組
*/
export const controllers = [];
/**
* 給controller添加裝飾
* @param {*} path
*/
export function Controller(path=""){
return function(target){
// 給controller類(lèi)添加路由前綴
console.log(target)
target.prefix = path;
}
}
/**
* 給controller類(lèi)的方法添加裝飾
* url 可選
* method 請(qǐng)求方法
* middleware 中間件
*/
export function RequestMapping({url="",method="",middleware=[]}){
return function(target,name,descriptor){
let path = "";
// 判斷有沒(méi)有定義url
if(!url){
// 取方法名作為路徑
path = `/${name}`;
}else{
// 自己定義的url
path = url;
}
// 創(chuàng)建router需要的數(shù)據(jù) url,method,middleware(可以沒(méi)有),最終執(zhí)行的方法,裝飾器隊(duì)對(duì)象的構(gòu)造函數(shù)
const item = {
url:path,
method:method,
middleware:middleware,
handler:target[name],
constructor:target.constructor,
};
controllers.push(item);
}
}
路由的統(tǒng)一創(chuàng)建
創(chuàng)建router文件夾,再繼續(xù)創(chuàng)建index.js文件
no bi bi ,上代碼:
此文件是統(tǒng)一創(chuàng)建路由的入口文件,需要傳入koa實(shí)例和koa-router實(shí)例進(jìn)行創(chuàng)建路由和路由裝箱
import { controllers } from "./../common/decorator"
/**
* 初始化路由
*/
export default (app,router) => {
controllers.forEach((item) => {
// 獲取每個(gè)路由的前綴
const prefix = item.constructor.prefix;
let url = item.url;
if(prefix) url = `${prefix}${url}`; // 組合真正鏈接
console.log(item.method,url); // 打印請(qǐng)求的路由method,url
router[item.method](url, ...item.middleware, item.handler); // 創(chuàng)建路由
});
app.use(router.routes()).use(router.allowedMethods()) // 路由裝箱
}
然后在 app.js進(jìn)行初始化路由:
initRoutes(app,router)
使用
創(chuàng)建controler/index.js , middleware/index.js
middleware/index.js的測(cè)試代碼
export default async (ctx,next)=>{
console.log("middleware")
await next();
}
controler/index.js的測(cè)試代碼:
import {RequestMethod,Controller,RequestMapping } from "./../common/decorator/index"
import TestFun from './../middleware/index'
// 添加Controller前綴
@Controller("/api/test")
export default class TestController {
// 基本面使用 /api/test/login
@RequestMapping({
url:"/login",
method:RequestMethod.GET, // 定義請(qǐng)求方法
})
async login(ctx){
ctx.body = {
code:0,
message:"success"
}
}
// 定義有中間件的router /api/test/test
@RequestMapping({
method:RequestMethod.POST, // 定義請(qǐng)求方法
middleware: [TestFun] // 使用中間件
})
async test(ctx){
ctx.body = {
code:0,
message:"success"
}
}
}
把conttroller/index.js 在 router/index.js導(dǎo)入并導(dǎo)出
export * from "../controller/index"
如果還有其它c(diǎn)ontroller,那么只需要在router/index.js添加就可以了~
重啟服務(wù)

使用postman測(cè)試


ok,搞定~,以后再也不用寫(xiě)router啦,感覺(jué)爽了很多!
源碼請(qǐng)點(diǎn)擊gitHub地址
代碼注釋很詳細(xì),若有什么不理解或者更好的建議,歡迎各位留言交流共同學(xué)習(xí)~