Express 源码浅析

Express 源码浅析

  • 基于 Node.js 平台,快速、开放、极简的 web 开发框架
  • 简单几行代码搞定一个http服务
var express = require('express');
var app = express();

app.get('/', function (req, res) {
  res.send('Hello World!');
});

var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;

  console.log('Example app listening at http://%s:%s', host, port);
});

what‘s app?

  • application.js 实例

    • get方法分析 methods是一个数组,里面基本包含里所有http请求方式,当get方法只有一个参数的时候,实际上是取application实例中的变量值。

         methods.forEach(function(method){
           app[method] = function(path){
             if (method === 'get' && arguments.length === 1) {
               // app.get(setting)
               return this.set(path);
             }
      
             this.lazyrouter();
      
             var route = this._router.route(path);
             route[method].apply(route, slice.call(arguments, 1));
             return this;
           };
         });
      
    • 先看下this.layrouter() 干了什么

           app.lazyrouter = function lazyrouter() {
            if (!this._router) {
              this._router = new Router({
                caseSensitive: this.enabled('case sensitive routing'),
                strict: this.enabled('strict routing')
              });
      
              this._router.use(query(this.get('query parser fn')));
              this._router.use(middleware.init(this));
            }
          };
      
      1. 创建一个Router实例,并赋值给app._router;
      2. Router可以看作是一个中间件容器,router.stack = [] 这个数组里面存放中间件
      3. 并调用两次use,存放了非路由中间件
      
    • 接下来看this._router.route(path)

        ```
        proto.route = function route(path) {
        var route = new Route(path);
      
        var layer = new Layer(path, {
          sensitive: this.caseSensitive,
          strict: this.strict,
          end: true
        }, route.dispatch.bind(route));
      
        layer.route = route;
      
        this.stack.push(layer);
        return route;
      };
      ```
      1. 创建Route实例,并layer放入Router.stack数组中
      2. 调用use/route方法 都会创建一个layer实例,区别是layer.route = undefined;layer.route = route;
      
      • route[method].apply(route, slice.call(arguments, 1));

           methods.forEach(function(method){
            Route.prototype[method] = function(){
              var handles = flatten(slice.call(arguments));
        
              for (var i = 0; i < handles.length; i++) {
                var handle = handles[i];
        
                if (typeof handle !== 'function') {
                  var type = toString.call(handle);
                  var msg = 'Route.' + method + '() requires callback functions but got a ' + type;
                  throw new Error(msg);
                }
        
                debug('%s %o', method, this.path)
        
                var layer = Layer('/', {}, handle);
                layer.method = method;
        
                this.methods[method] = true;
                this.stack.push(layer);
              }
        
              return this;
            };
          });
        
        1. 假设是method==get, 调用route.get 也会创建一个layer,并把layer放到route.stack数组中。
  • 小结 application实例化时,会根据methods数组里的函数名,把函数赋在application上,调用app.use() app.get() app.post()... 第一次会创建一个唯一的Router实例,实例中有个stack数组,数组中存放的是封装在layer实例中的各种中间件,其中路由中间件的layer中包含一个route实例,同时route实例也有个stack数组.

what's router?

  • 每个application实例只有一个Router.

      var proto = module.exports = function(options) {
           var opts = options || {};
    
           function router(req, res, next) {
             router.handle(req, res, next);
           }
    
           // mixin Router class functions
           setPrototypeOf(router, proto)
    
           router.params = {};
           router._params = [];
           router.caseSensitive = opts.caseSensitive;
           router.mergeParams = opts.mergeParams;
           router.strict = opts.strict;
           router.stack = [];
    
           return router;
     };
    

    在使用路由的时候,我们可以这么做:

     var router = express().Router;
     router.get('/user/login', user.login);
    
     app.use('/',router);
    

    上面实际上是添加了一个非路由中间件,把一个新的Router实例,加到了router.stack数组里。在中间件执行过程中,会调用

     layer.handle_request(req, res, next);
    
     Layer.prototype.handle_request = function handle(req, res, next) {
       var fn = this.handle;
    
       if (fn.length > 3) {
         // not a standard request handler
         return next();
       }
    
       try {
         fn(req, res, next);  //关键在这个地方,fn就是router函数
       } catch (err) {
         next(err);
       }
     };
    

    当一个http请求进来时,会调用router.handle(),这个函数会依次执行stack中符合条件的layer.handle_request. 如果是路由中间件,会调用之前route.dispatch的函数。这里不是路由中间件,又会回到router中调用到router.handle()函数,重复之前的行为。 router中的一个非路由中间件是一个router实例,听起来有点绕。

2018-3-26 18:11:27 浏览(229)
Copyright ©leiwei | 京ICP备18013719号