Connect架构初探 for 新手--因为我是新手,写的是给自己看,所以啰嗦点,有错望指正
发布于 3 年前 作者 bigmusic 3669 次浏览

前言:
我发现,原来写文档也是技术活,因为不但要自己看得明白,别人也要明白,
而且不能啰嗦,要简洁和条理清晰,对于我文盲来说,实在纠结,将就看吧

在我看来,无论任何框架,只要我不知道它的内部运行机理,我都觉得不舒服
那些神马’回收机制不用你管’的话就别给我说了,就算源码看不懂,起码逻辑上要明白吧?
记得刚开始碰到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!
});
回到顶部