剛接觸nodejs不久,在后端的web框架中棒掠,有接觸過express孵构,koa,sails等一些框架烟很。
express簡單颈墅,易上手,擴展也很強溯职,但是寫法規(guī)范很多精盅。
koa這次支持了es7的異步,終于可以擺脫回調的煩惱谜酒。
sails作為api服務端叹俏,功能很強大,但是文檔不是太多僻族,當時候接觸的時候因為插件的版本號踩過很多坑粘驰,所以不是太喜歡。
eggjs述么,阿里出品蝌数,基于koa,整理了解之后感覺不錯選擇度秘,文檔也比較齊全顶伞。
主要記錄eggjs中實際應用可能碰到的問題
快速開始
見官方文檔,就不再重復
eggjs官網文檔
路由
eggjs路由是通過router.js這個文件來配置信息剑梳。
其中源碼中有一段restful router api的代碼:
resources(...args) {
const splited = spliteAndResolveRouterParams({ args, app: this.app });
const middlewares = splited.middlewares;
// last argument is Controller object
const controller = splited.middlewares.pop();
let name = '';
let prefix = '';
if (splited.prefix.length === 2) {
// router.get('users', '/users')
name = splited.prefix[0];
prefix = splited.prefix[1];
} else {
// router.get('/users')
prefix = splited.prefix[0];
}
for (const key in REST_MAP) {
const action = controller[key];
if (!action) continue;
const opts = REST_MAP[key];
let formatedName;
if (opts.member) {
formatedName = inflection.singularize(name);
} else {
formatedName = inflection.pluralize(name);
}
if (opts.namePrefix) {
formatedName = opts.namePrefix + formatedName;
}
prefix = prefix.replace(/\/$/, '');
const path = opts.suffix ? `${prefix}/${opts.suffix}` : prefix;
const method = Array.isArray(opts.method) ? opts.method : [ opts.method ];
this.register(path, method, middlewares.concat(action), { name: formatedName });
}
return this;
}
會去自動掛在restful router api的映射唆貌。
在學習sails中,會發(fā)現(xiàn),它的路由配置是通過配置文件控制的,如果不配置逢捺,也會自動去加載controllers下的控制器映射到路由中去扣。所以在當時候自己用express去嘗試寫了一個簡單自動加載的機制。
代碼如下:
var _initGlobals = function (app) {
var models = app.get('models');
for (var i in models) {
global[tools.firstUpperCase(i)] = models[i]
}
};
var _initAction = function (name, cObj, router) {
var keys = Object.keys(cObj)
if (actions) {// 映射普通action路由
_initActionApi(cObj, keys, router)
}
if (rest) {// 映射rest 路由
_initRestApi(cObj, keys, router)
}
};
var _initActionApi = function (cObj, keys, router) {
keys.forEach(function (key) {
['get', 'post', 'put', 'delete'].forEach(function (m) {
router[m]('/' + key, function (req, res) {
cObj[key](req, res)
})
})
})
};
var _initRestApi = function (cObj, keys, router) {
router.get('/', function (req, res) {
// cObj.find(req, res)
_.bind(cObj.find, this, req, res)();
})
router.post('/', function (req, res) {
_.bind(cObj.create, this, req, res)();
})
router.get('/:id', function (req, res) {
_.bind(cObj.findOne, this, req, res)();
})
router.put('/:id', function (req, res) {
_.bind(cObj.update, this, req, res)();
})
router.delete('/:id', function (req, res) {
_.bind(cObj.destroy, this, req, res)();
})
};
var _init = function (app) {
_initWSRouter(app);
_initUpload(app);// 初始化通用的上傳
var controllerPath = path.join(process.cwd(), '/api', '/controllers');// 控制器的基礎路徑
var files = fs.readdirSync(controllerPath)// 讀取文件
var name, cObj, router;
files.forEach(function (file) {
name = file.replace('Controller.js', '')// 在controllers目錄下都是以Controller.js結尾命名的控制器,例如:UserController.js
cObj = require(controllerPath + '/' + file.replace('.js', '')) // 動態(tài)加載控制器js
router = express.Router()
_initAction(name, cObj, router)// 初始化路由
if (prefix) {// 是否統(tǒng)一添加路由前綴
app.use('/' + prefix + '/' + name.toLowerCase(), router)
// app.use('/' + name.toLowerCase(), router)
} else {
app.use('/' + name.toLowerCase(), router)
}
})
}
//api/controller/UserController.js
module.exports = {
find: function (req, res) {
res.json({type: 'find'})
},
findOne: function (req, res) {
res.json({type: 'findOne'})
},
create: function (req, res) {
res.json({type: '創(chuàng)建'})
},
update: function (req, res) {
res.json({type: 'update'})
},
destroy: function (req, res) {
res.json({type: 'destroy'})
}
}
基于以上方案,所以正對于eggjs路由,一個想到的采用同樣方式
簡單自動裝載
修改router.js使用上面方式骂倘。好,開始碼代碼...
...
終于碼完了油猫,測試完成稠茂,話不多說,代碼如下
'use strict'
const fs = require('fs')
const path = require('path')
const _initAction = function (reqPath, obj, router, controller) {
const keys = Object.getOwnPropertyNames(obj.prototype)
let c = controller
const l = reqPath.split('/')
l.splice(0, 1)
l.forEach(v => {// 循環(huán)獲取到對應的路由控制器對象
c = c[v]
})
keys.forEach(function (key) {
if (key !== 'constructor') {// 去除掉構造函數(shù)
const controllerMethod = c[key]
if (controllerMethod) {// 在eggjs中,得到的keys會多出pathName睬关,fullPath诱担,所以過濾下
['get', 'post'].forEach(function (m) {// 定義路由的get,post电爹,也可以擴展put蔫仙,delete等
router[m](`${reqPath}/${key}`, controllerMethod)
})
}
}
})
// 這個地方,掛在restful api丐箩,但是沒有驗證摇邦,是否會和上面action綁定有沒有沖突,理論上命名不沖突屎勘,就不會有問題
router.resources(l[l.length - 1], `${reqPath}`, c)
}
/**
* @param {Egg.Application} app - egg application
*/
module.exports = app => {
const {router, controller} = app
const controllerPath = path.join(process.cwd(), '/app', '/controller/sys') // 這個地方應該去做遞歸把所有js文件遍歷出來
const files = fs.readdirSync(controllerPath)
let reqPath
let cObj
files.forEach(function (file) {
reqPath = controllerPath + '/' + file.replace('.js', '')
/**
*reqPath施籍,用來通過controller中的路徑作為請求路徑
*例如:文件路徑:/app/controller/sys/user.js 那么得到就是/sys/user
*/
reqPath = reqPath.substr(reqPath.indexOf('controller') + 10).replace(/\\/, '/')
cObj = require(controllerPath + '/' + file.replace('.js', ''))
_initAction(reqPath, cObj, router, controller)
})
// require('./router/sys/user')(app)
// require('./router/sys/permissions')(app)
}
總結
基本上可以通過這個方式實現(xiàn)自動加載路由配置實現(xiàn)。從這個中間還可以去擴展配置出類似與sails那樣的路由配置風格概漱。
做這個東西就是為了偷懶丑慎,寫好之后,可以不用再去管路由配置問題瓤摧,嗯竿裂,少了一些事,未對性能這塊進行測試照弥,讀取加載應該影響不大腻异,下次再看看是否可以封裝成插件,有時間再來研究~~~~