个人认为该框架可以主要分为两个部分理解
1、初始化阶段(把路由回调函数存入对应的路由中的栈)
2、匹配以及触发路由回调函数阶段
说明:
引入express的时候实际上引入的是Application这个应用。在Application中,创建Router,router中用stack来存放layer,一开始在应用的时候引入的app.get('/', handlers) 每遇到一个就创建一个layer,layer中以路径,以及触发进入router的dispatch函数为变量。
当路径匹配到的时候,触发dispatch进入route, route中也存在一个stack存放layer,但是这里的layer在初始化的时候就得加上method,因为到时匹配的时候,该层主要通过method来匹配,而现在里面layer触发的是handler也就是app.get('/', handlers) 中的callback执行。
而当路径匹配不到的时候,跳到下一层layer进行匹配,重复上面的步骤。
express使用(不详细说,这个看文档调API即可)
let express = require('./lib/express.js')
let app = express();
app.get('/', function(req, res, next){
console.log('1');
next();
})
app.get('/', function(req, res,next){
console.log('2');
next();
},function(req, res){
res.end('3');
})
// 1 2 3
app.listen(3000, function(){
console.log('server start at 3000');
})
源码解析
express.js
const Application = require('./application')
function createApplication() {
return new Application()
}
module.exports = createApplication;
application.js(创建应用)
这里只演示get方法,其他方法都异曲同工
let http = require('http');
let Router = require('./router/index')
//Application是express的整个应用系统
//应用和路由分离
function Application() {
//私有变量用下划线
//创建路由系统
this._router = new Router()
}
//1、初始化阶段:初始化阶段(把路由回调函数存入对应的路由中的栈)
// 把路由也就是app.get('/', function这些),放进router的stack里面
Application.prototype.get = function(path, ...handlers) {
this._router.get(path, handlers);
}
//2、匹配以及触发路由回调函数阶段
Application.prototype.listen = function() {
let server = http.createServer((req, res) => {
// 如果匹配不到所有的路由触发done
function done(){
res.end(`Cannot ${req.url} ${req.method}`);
}
// 匹配路由并执行对应的回调函数
this._router.handle(res,req,done);
})
server.listen(...arguments);
// http.createServer的listen是内置方法,以上路由handler完了之后的调callback
}
module.exports = Application;
./router/index.js
// 路由系统
const Route = require('./route');
const Layer = require('./layer.js');
const url = require('url')
function Router() {
this.stack = [];
}
//创建route和Layer的关系
Router.prototype.route = function(path) {
let route = new Route();
//如果不绑,直接到window或者Router,所以指向route
//如果Layer匹配到了,放到route处理 route.dispatch.bind(route)
let layer = new Layer(path, route.dispatch.bind(route));
layer.route = route; // 把route放到layer上
this.stack.push(layer); //layer放到数组中
return route;
}
//创造layer,每个Layer上有个route,将get的handler传入route,在route中存起来
Router.prototype.get = function(path,handlers) {
//方法引用也得整this *****************
let route = this.route(path);
route.get(handlers);
}
// 匹配路由并执行对应的回调函数
//out就是done(找不到路由就跳出)
Router.prototype.handle = function(req,res,out) {
//请求的时候执行此方法
let {pathname} = url.parse(req.url);
let idx = 0;
//遍历router中的stack,匹配符合与请求符合的路由
let next = () =>{
if(idx >= this.stack.length) {
return out();
}
let layer = this.stack[idx++];
//判断请求路径是否路由匹配
if (layer.match(pathname)) {
// 返回的是layer.handler() 即 第14行 上面的Router.prototype.route中route.dispatch方法 route.dispatch.bind(route)(res,req,out)
layer.handle_request(res,req,next); // next是外层的下一层(也就是该层)
//这里只判断了路径没有判断方法,等到下一层才判断方法
}else {
next()
}
}
next()
}
module.exports = Router;
layer.js
function Layer(path,handler) {
this.path = path;
this.handler = handler;
}
//路径匹配
Layer.prototype.match = function(pathname) {
return pathname === this.path;
}
Layer.prototype.handle_request = function(req, res,next){
//。。。。to do
//包装切片
this.handler();
}
module.exports = Layer;
route.js
let Layer = require('./layer.js')
function Route() {
this.stack = [];
}
Route.prototype.get = function(handlers) {
handlers.forEach(handler => {
//给route中添加layer,这个层中需要存放方法名和handler
let layer = new Layer('/',handler);
layer.method = 'get';
//挂方法,因为之后在第二阶段,这个时候只需要判断方法即可
this.stack.push(layer);
});
}
// 该层判断方法(上一层已经判断过路径了)
Route.prototype.dispatch = function(res,req,out) {
let idx = 0;
let next = () => { //用户调用的next, 如果调用next,会执行内层中的next方法,如果没有匹配会调用外层的next方法
if (idx >= this.stack.length) return out();
let layer = this.stack[idx++];
if(layer.method === req.method.toLowerCase()){
//layer外层存的是dispatch,里层存的是handler(这里存的就是handler并且执行)
layer.handle_request(req, res, next);
}else{
next();
}
}
next()
}
module.exports = Route;
优缺点
优点:线性逻辑,通过中间件形式把业务逻辑细分、简化,一个请求进来经过一系列中间件处理后再响应给用户,清晰明了。并且相较于KOA来说用户基数较多
缺点:基于 callback 组合业务逻辑,业务逻辑复杂时嵌套过多,异常捕获困难