阅读 155

nodejs-express(5个核心问题)

express

本节一起来探究node中常用的express核心中的5个问题

1. 调用 express() 到底创建了什么❓

打开express core的 lib/express.js 会发现有这么一段代码

exports = module.exports = createApplication;
function createApplication() {
  var app = function(req, res, next) {
    app.handle(req, res, next);
  };

  mixin(app, EventEmitter.prototype, false);
  mixin(app, proto, false);

  // expose the prototype that will get set on requests
  app.request = Object.create(req, {
    app: { configurable: true, enumerable: true, writable: true, value: app }
  })

  // expose the prototype that will get set on responses
  app.response = Object.create(res, {
    app: { configurable: true, enumerable: true, writable: true, value: app }
  })

  app.init();
  return app;
}
复制代码

很明显在调用 express()本质是执行了一段 createApplication 函数

2. app.listen()做了哪些事情❓

这里你可能会有两个疑问:

2.1 app不是一个函数吗❓为什么可以去调用 listen

app虽然是一个函数,其实也是一个对象,故而可以去调用 listen

2.2 在createApplication中并没有发现app中有listen函数

app可以直接调用listen 这里有一段很重要的处理即:

mixin(app, proto, false); //底层做了混入处理
复制代码

proto 本质是 application,在 application.js中其本质就是调用了 http.createServer(this).listen 把参数全部传递了过来; 依据:

app.listen = function listen() {
  var server = http.createServer(this);
  return server.listen.apply(server, arguments);
};

复制代码

3. app.use(中间件)内部发生了什么❓

open application.js 在 app.use = function use() 函数中生成了一个全局路由 router,在 var fns = flatten(slice.call(arguments, offset)) 做了扁平处理,将所有函数赋值给了 fns,接着通过 fns.forEach()取出每一个函数在228行本质调用了 router.use()函数

router.use(path, function mounted_app(req, res, next) {
      var orig = req.app;
      fn.handle(req, res, function (err) {
        setPrototypeOf(req, orig.request)
        setPrototypeOf(res, orig.response)
        next(err);
      });
    });
复制代码

跳到lib/router/index.js你的疑问即可揭晓。在其底层proto.use = function use(fn)中 其实是 new Layer() ,然后将生成的图层 layer push到了 stack中,那么这个 stack就是初期绑定到router原型上的一个 stack数组,layer中我们的回调函数,this.stack.push(layer)这里的 this本质就是router对象。

4. 用户发送了请求,中间件是怎么被回调的❓

当一个请求过来后,底层本质是处理了 router中的handle函数 router.handle(req, res, done);proto.handle() 函数中 通过self.stack拿到我们的 stack,第一次主动调用 next() 核心函数:

  // idx = 0 ; // init 
  function next(err) {
    var layerError = err === 'route'
      ? null
      : err;

    // remove added slash
    if (slashAdded) {
      req.url = req.url.substr(1);
      slashAdded = false;
    }

    // restore altered req.url
    if (removed.length !== 0) {
      req.baseUrl = parentUrl;
      req.url = protohost + removed + req.url.substr(protohost.length);
      removed = '';
    }

    // signal to exit router
    if (layerError === 'router') {
      setImmediate(done, null)
      return
    }

    // no more matching layers
    if (idx >= stack.length) {
      setImmediate(done, layerError);
      return;
    }

    // get pathname of request
    var path = getPathname(req);

    if (path == null) {
      return done(layerError);
    }

    // find next matching layer
    var layer;
    var match;
    var route;

    while (match !== true && idx < stack.length) {
      layer = stack[idx++];
      match = matchLayer(layer, path);
      route = layer.route;

      if (typeof match !== 'boolean') {
        // hold on to layerError
        layerError = layerError || match;
      }

      if (match !== true) {
        continue;
      }

      if (!route) {
        // process non-route handlers normally
        continue;
      }

      if (layerError) {
        // routes do not match with a pending error
        match = false;
        continue;
      }

      var method = req.method;
      var has_method = route._handles_method(method);

      // build up automatic options response
      if (!has_method && method === 'OPTIONS') {
        appendMethods(options, route._options());
      }

      // don't even bother matching route
      if (!has_method && method !== 'HEAD') {
        match = false;
        continue;
      }
    }

    // no match
    if (match !== true) {
      return done(layerError);
    }

    // store route for dispatch on change
    if (route) {
      req.route = route;
    }

    // Capture one-time layer values
    req.params = self.mergeParams
      ? mergeParams(layer.params, parentParams)
      : layer.params;
    var layerPath = layer.path;

    // this should be done for the layer
    self.process_params(layer, paramcalled, req, res, function (err) {
      if (err) {
        return next(layerError || err);
      }

      if (route) {
        return layer.handle_request(req, res, next);
      }

      trim_prefix(layer, layerError, layerPath, path);
    });
  }
复制代码

找到之后又调用了 layer 的handle_request()函数,这个函数又在处理什么呢?接着往下看。其实他做的事情本质上是调用核心fn(req, res, next); fn是什么? fn就是从 layer中取出的 handle

5. next为什么会自动执行下一个中间件❓

理解了第4个问题流程,当我们自己注册了中间件,调用了 next() 框架底层就会来到next()匹配下一个stack进行layer的执行

文章分类
前端
文章标签