前言:
我发现,原来写文档也是技术活,因为不但要自己看得明白,别人也要明白,
而且不能啰嗦,要简洁和条理清晰,对于我文盲来说,实在纠结,将就看吧
在我看来,无论任何框架,只要我不知道它的内部运行机理,我都觉得不舒服
那些神马’回收机制不用你管’的话就别给我说了,就算源码看不懂,起码逻辑上要明白吧?
记得刚开始碰到jQuery的时候,网上有些介绍jQuery设计思路的文章,代码几行就把整个框架描出来了
so,这里我也尽量简洁一些,但Connect是Callback的思路,我已经尽力了
Express是基于Connect的扩充,所以理解了Connect的架构,定制自己的Express就不难了
但官方Api实在很懒,这也不能说不好,毕竟这些源码对于大牛来说实在简单,看Api不如看源码
btw,因为这里都是大牛,所以这里会贴一个简洁一点的版本
啰嗦版请移步Github:
https://github.com/bigmusic/Web-Readme-zh-cn/blob/master/connect-2-7-10_13-05-21/Readme.md
#Connect的框架架构, 可简单归结为以下伪代码
此篇幅全部为伪代码
var connect,
app,
http = {};
调用createServer
//伪代码
http.createServer = function(callback){
var req, res, next;
//此处省去N行代码,好奇的自觉去看Node.js源码
//doSomething with req/res/next
EventEmitter.on('connection', callback(req, res, next));
};
调用connect(),并取其返回值传给myApp
myApp = connect()//其实就是返回connect内部的app
myApp.use(connect.session({
secret: 'session',
cookie: {maxAge: year
}));
http.createServer(myApp);
往下看的时候注意Handle和handle的不同,源码都用handle,很混肴思路
调用connect()返回的是connect作用域内的函数app,
当调用createServer将myApp作为Callback调用时,
实际上是调用connect()返回的app();
而调用此app()实质上是调用proto.js里面的Handle方法
app.Handle是通过utils.merge(app, proto);作为方法合并进app的
也就是说,createServer最终调用的是proto.js里的Handle方法!stack则是储存中间件(middleware)的堆栈
而调用proto.Handle方法会遍历这个stack堆栈把中间件按顺序执行
connect = function(){
function app(req, res, next){
app.Handle(req, res, next);//其实是proto.Handle(req,res,next);
};
utils.merge(app, proto);
utils.merge(app, EventEmitter.prototype);
app.route = '/';
app.stack = [];
return app; //调用connect()是返回app函数
};
proto.js里面的伪代码简化:
调用myApp.use()时,参数为中间件,如myApp.use(connect.session());注意:这里myApp.use()的参数其实是中间件返回的函数对象而非函数本身
可以看作myApp.use(sessionReturn)==myApp.use(connect.session())如下伪代码:
connect.session = function(options){ var options.something etc..... store.generate = function(req){...}; //define something return function sessionReturn(req, res, next){ //doSomething with options }; };
myApp.use方法会按用户的代码逻辑顺序把中间件返回的函数逐个放进stack堆栈,
此处为伪代码省去判断route的代码:
proto.use = function(route, fn){
this.stack.push({ //把fn放进stack堆栈
route: route,
handle: fn //fn在这个例子里其实就是sessionReturn
});
return this;//返回指针可以让.use链式调用
};
此为实际调用myApp()的内容,可观察connect函数返回的app对象
function app(req, res, next){
app.Handle(req, res, next);//proto.Handle(req,res,next);
};
调用myApp()=app()时其实是在调用proto.Handle方法
这个方法会遍历调用myApp.use()后stack堆栈中每一个用户用到的middleware
按用户代码的逻辑顺序逐个运行
改用proto.Handle,注意这不是构造函数,只是为了区分
proto.Handle = function(req, res, out){
var stack = this.stack,
index = 0;
function next(err){
var layer;
layer = stack[index++];
if(!layer)return someThing; //遍历完后跳出函数
layer.handle(req, res, next);//这里可以之间看成sessionReturn(req,res,next);
};
next();//重新调用next()遍历堆栈stack
};
当然,connect.js会用require(‘fs’),把middleware目录里的中间件全部悉数export
#最后尝试写下最简单的顺序逻辑伪代码:
http.createServer = function(callback){
var req, res, next;
//Listen the port and doSomething
EventEmitter.on('connection', callback(req, res, next));
};
http.createServer(function(req,res,next){
connect.session = function({secret: 'session',cookie: {maxAge: year}){
var options = {},
options.secret = arguments.secret;//....etc
//doSomething
return function sessionReturn(req, res, next){
//dosomething
};
};
myApp = connect();
myApp.use = function(route, connect.session())
{
this.stack.push({
route: route,
handle: function sessionReturn(req, res, next){
//dosomething
}
});
};
myApp = connect() = function(req, res, next){
proto.Handle = function(req, res, out){
this.stack.handle(req, res, next);
};
proto.Handle(req, res, next);
};
myApp(req,res,next);//go!
});