js中間件
當我們在編寫業務代碼時候,我們無法避免有些業務邏輯復雜而導致業務代碼寫得又長又亂,如果再加上時間緊湊情況下寫出來的代碼估計會更讓人抓狂。以至於我們一直在尋求更好的架構設計和更好的代碼設計,這是一個沒有終點的求知之路,但是在這條路上會越走越好。
在前端,我們可以借用這種思想通過before和after函數來實現:
Function.prototype.before = function(fn){//函數處理前執行fn
var self = this;
return function(){
fn.call(this);
self.apply(this, arguments);
}
}
Function.prototype.after = function(fn){//函數處理后執行fn
var self = this;
return function(){
self.apply(this, arguments);
fn.call(this);
}
}
實現思路是對被處理的函數通過閉包封裝在新的函數里,在新的函數內部按照順序執行傳入的參數fn和被處理的函數。
1 舉個例:
用戶提交表單數據之前需要用戶行為統計,代碼應該是這樣寫:
function report(){
console.log('上報數據');
}
function submit(){
console.log('提交數據');
}
submit.before(report)(); //提交之前執行report
//結果: 上報數據
// 提交數據
從代碼可以看出已經把統計和數據提交業務隔離起來,互不影響。
但是如果提交數據之前,需要數據驗證並且依據驗證結果判斷是否能提交,怎么做?這里要改動before函數,看下代碼:
Function.prototype.before = function(fn){//函數處理后執行fn
var self = this;
return function(){
var res = fn.call(this);
if(res)//返回成功則執行函數
self.apply(this, arguments);
}
}
function report(){
console.log('上報數據');
return true;
}
function validate(){
console.log('驗證不通過');
return false;
}
function submit(){
console.log('提交數據');
}
submit.before(report).before(validate)();
//結果:
// 驗證不通過
function report(){
console.log('上報數據');
return true;
}
function validate(){
console.log('驗證通過');
return true;
}
function submit(){
console.log('提交數據');
}
submit.before(report).before(validate)();
//結果:
// 驗證通過
// 上報數據
// 提交數據
上面的例子如果很復雜會出現很長的鏈式,后期維護也很容易看暈,並且before和after也沒有考慮到異步操作,顯然還是有些不足的,那么還有沒有其他解決辦法呢,既能隔離業務,又能方便清爽地使用~我們可以先看看其他框架的中間件解決方案。
2 express
express是非常輕量的框架,express是集合路由和其他幾個中間件合成的web開發框架,koa是express原班人馬重新打造一個更輕量的框架,所以koa已經被剝離所有中間件,甚至連router中間件也被抽離出來,任由用戶自行添加第三方中間件。解析express的寫法
express的中間件寫法如下:
var express = require('express');
var app = express();
app.use(function(req, res, next) {
console.log('數據統計');
next();//執行權利傳遞給
});
app.use(function(req, res, next) {
console.log('日志統計');
next();
});
app.get('/', function(req, res, next) {
res.send('Hello World!');
});
app.listen(3000);
//整個請求處理過程就是先數據統計、日志統計,最后返回一個Hello World!
從上圖來看,每一個“管道”都是一個中間件,每個中間件通過next方法傳遞執行權給下一個中間件,express就是一個收集並調用各種中間件的容器。
中間件就是一個函數,通過express的use方法接收中間件,每個中間件有express傳入的req,res和next參數。如果要把請求傳遞給下一個中間件必須使用 next() 方法。當調用res.send方法則此次請求結束,node直接返回請求給客戶,但是若在res.send方法之后調用next方法,整個中間件鏈式調用還會往下執行,因為當前hello world所處的函數也是一塊中間件,而res.send只是一個方法用於返回請求。
3 參照express我們可以仿寫
我們可以借用中間件思想來分解我們的前端業務邏輯,通過next方法層層傳遞給下一個業務。做到這幾點首先必須有個管理中間件的對象,我們先創建一個名為Middleware 的對象:
function Middleware(){
this.cache = [];
}
Middleware通過數組緩存中間件。下面是next和use 方法:
Middleware.prototype.use = function(fn){
if(typeof fn !== 'function'){
throw 'middleware must be a function';
}
this.cache.push(fn);
return this;
}
Middleware.prototype.next = function(fn){
if(this.middlewares && this.middlewares.length > 0 ){
var ware = this.middlewares.shift();
ware.call(this, this.next.bind(this));
}
}
Middleware.prototype.handleRequest = function(){//執行請求
this.middlewares = this.cache.map(function(fn){//復制
return fn;
});
this.next();
}
我們用Middleware簡單使用一下:
var middleware = new Middleware();
middleware.use(function(next){console.log(1);next();})
middleware.use(function(next){console.log(2);next();})
middleware.use(function(next){console.log(3);})
middleware.use(function(next){console.log(4);next();})
middleware.handleRequest();
//輸出結果:
//1
//2
//3
//
4沒有出來是因為上一層中間件沒有調用next方法,我們升級一下Middleware 高級使用
var middleware = new Middleware();
middleware.use(function(next){
console.log(1);next();console.log('1結束');
});
middleware.use(function(next){
console.log(2);next();console.log('2結束');
});
middleware.use(function(next){
console.log(3);console.log('3結束');
});
middleware.use(function(next){
console.log(4);next();console.log('4結束');
});
middleware.handleRequest();
//輸出結果:
//1
//2
//3
//3結束
//2結束
//1 結束
每一個中間件執行權利傳遞給下一個中間件並等待其結束以后又回到當前並做別的事情,方法非常巧妙。