0

0

0

修罗

站点介绍

只有了解事实才能获得真正的自由

express源码阅读(一)

修罗 2022-07-10 1904 0条评论 nodejs

首页 / 正文

express源码阅读(一)

下载源码:https://github.com/expressjs/express

git clone https://github.com/expressjs/express.git

个人总结的源码阅读方法:不要多牛逼的技术,耐心最为重要。

一、官方例子

var express = require("../../index");
var app = express();

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

app.listen(3000, function () {
  console.log("Express started on port 3000");
});

分析:

  • express函数调用做了啥?
  • get方法怎么执行,如何处理路径和回调的?
  • listen监听端口怎么实现的?
  • 浏览器请求路径又是如何匹配的?

二、express函数调用

var express = require("../../index");
var app = express();

2.1 找到require("../../index")中的导入的index.js

module.exports = require('./lib/express');

1638368793971.png

2.2 找到lib/express文件

1638369414039.png

2.2.1 express中的代码解析

1. 导出模块

// 导出createApplication函数
exports = module.exports = createApplication;

2. express()调用后返回的函数

var app = function (req, res, next) {
  // 暂时不用关注,用户请求才走到这里
  app.handle(req, res, next);
};

3. mixin

合并到原对象(原对象,合并对象,false原对象有则重写)

// 使app有发布订阅的功能
mixin(app, EventEmitter.prototype, false);

// 混入application的属性
mixin(app, proto, false);

EventEmitterprote来自模块导入:

var EventEmitter = require("events").EventEmitter;
var proto = require("./application");

4. 初始化配置

app.init();

init方法来自proto的mixin混入,proto来自./application

2.2.2 application.js

app.init = function init() {
  this.cache = {};
  this.engines = {};
  this.settings = {};

  this.defaultConfiguration();
};

上面把app当对象用了,我写个例子说明类似情况:

function app() {}
app.usename = "修罗";
app.getUsername = function () {
  console.log(this); // function app(){}
  console.log(this.usename); // "修罗"
};

app.getUsername();
  • this.defaultConfiguration();执行后给app添加了配置对象。
app.locals.settings =  
app.setting = {
    "x-powered-by": true,
    etag: "weak",
    "etag fn": function generateETag(body, encoding) {...},
    env: "development",
    "query parser": "extended",
    "query parser fn": function parseExtendedQueryString(str) { ...},
    "subdomain offset": 2,
    "trust proxy": false,
    "trust proxy fn": function trustNone() { return false; },
    view: function View(name, options) {...},
    views: "d:\\source\\express\\views",
    "jsonp callback name": "callback",
};
                                        
app.mountpath = "/";

三、app.get

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

3.1 方法定义在application.js

application模块加载执行:

  • 定义app.get、app.post等等方法

1638376135312.png

// method: [ 'get', 'post', 'put', 'head', 'delete', 'options'...]
methods.forEach(function (method) {
  app[method] = function(path){...}
});

3.2 例子:app.get

// 参数:"/", function (req, res) {}
app.get = function (path) {
    // app.get传一个参数
    if (method === "get" && arguments.length === 1) {
        return this.set(path);
    }

    // 初始化router,只执行一次
    this.lazyrouter();
    
    // 返回route对象
    var route = this._router.route(path);
    
    // 最后调route.get(fn):只传fn,不传path过去
    route.get.apply(route, slice.call(arguments, 1));
    
    // 把app返回,链式调用
    return this;
};

3.3 lazyrouter

1638377178705.png

3.3.1 创建Router对象

this._router = new Router({
    // 找setting中有没有,没有返回false,下面都false
    caseSensitive: this.enabled("case sensitive routing"),
    strict: this.enabled("strict routing"),
});
  • Router定义在router/index.js

new Routernew proto()

var proto = (module.exports = function (options) {
  var opts = options || {};

  function router(req, res, next) {
    router.handle(req, res, next);
  }

  // 内部函数router可以拿外部函数proto的属性
  setPrototypeOf(router, proto);

  router.params = {};
  router._params = [];
  router.caseSensitive = opts.caseSensitive;
  router.mergeParams = opts.mergeParams;
  router.strict = opts.strict;
  router.stack = [];
  // router: function(){}: { params..., __proto__: proto}
  return router;
});

3.3.2 router.use

this._router.use(query(this.get("query parser fn")));
this._router.use(middleware.init(this));

1. this._router.use(query(this.get("query parser fn")))

  • this.get("query parser fn")

this.get("query parser fn") 从setting中拿到函数

(str) => qs.parse(str, { allowPrototypes: true })
  • query()
query((str) => qs.parse(str, { allowPrototypes: true }))

query函数:

返回中间件(req, res, next)=>{}函数,函数内部调了next();

// 此中间件即在req上挂了一个属性:query,url参数解析为query对象。
// 如参数:username=xiuluo&pass=hui => { usename:xiuluo, pass:hui }
module.exports = function query(options) {
  ...
  return function query(req, res, next) {
    if (!req.query) {
      var val = parseUrl(req).query;
      req.query = queryparse(val, opts);
    }
    next();
  };
};
  • this._router.use传入中间件函数
proto.use = function use(fn) {
  var offset = 0;
  var path = "/";

  ...列出本例重要代码,省略部分代码

  // flatten([ fn ])
  // callbacks:[(req, res, next) { next() }]
  // 多维数组拍成一维数组
  var callbacks = flatten(slice.call(arguments, offset));

  // 遍历,每个中间件都创建了一个layer对象,并放到router的stack属性中
  for (var i = 0; i < callbacks.length; i++) {
    var fn = callbacks[i];

    var layer = new Layer(
      path,
      {
        // false,大小写不敏感
        sensitive: this.caseSensitive,
        strict: false,
        end: false,
      },
      fn
    );

    layer.route = undefined;

    this.stack.push(layer);
  }

  return this;
};

Layer

function Layer(path, options, fn) {
  // 不是通过new调用
  if (!(this instanceof Layer)) {
    return new Layer(path, options, fn);
  }

  var opts = options || {};

  // 保存中间件函数
  this.handle = fn;
  this.name = fn.name || "<anonymous>";
  this.params = undefined;
  this.path = undefined;
  // path的正则
  // /get/: id
  // pathRegexp("/get/:id").exec(/get/1) : [...] else null
  this.regexp = pathRegexp(path, (this.keys = []), opts);

  // set fast path flags
  this.regexp.fast_star = path === "*";
  this.regexp.fast_slash = path === "/" && opts.end === false;
}

2.this._router.use(query(this.get("query parser fn")))

middleware.init(this)

exports.init = function (app) {
  // 中间件:给请求响应对象挂一些属性
  return function expressInit(req, res, next) {
    // 响应头添加'x-powered-by'
    if (app.enabled("x-powered-by")) res.setHeader("X-Powered-By", "Express");

    req.res = res;
    res.req = req;
    req.next = next;

    setPrototypeOf(req, app.request);
    setPrototypeOf(res, app.response);

    res.locals = res.locals || Object.create(null);

    next();
  };
};

到此,lazyrouter执行完毕。

3.4 回到app.get()

// route对象挂载layer.route中
var route = this._router.route(path);

// route.get(fn):不传path过去
route[method].apply(route, slice.call(arguments, 1));

router.route

这里router存layer,layer存route,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;
};

Route

function Route(path) {
  this.path = path;
  this.stack = [];

  // route handlers for various http methods
  this.methods = {};
}

route.get(fn)

route存layer

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];

      var layer = Layer("/", {}, handle);
      layer.method = method;
      // route['method'] = true
      this.methods[method] = true;
      this.stack.push(layer);
    }

    return this;
  };
});

评论(0)


最新评论

  • 1

    1

  • 1

    1

  • -1' OR 2+158-158-1=0+0+0+1 or 'TKCTZnRa'='

    1

  • 1

    1

  • 1

    1

  • 1

    1

  • 1

    1

  • @@5Qa2D

    1

  • 1

    1

  • 1

    1

日历

2025年09月

 123456
78910111213
14151617181920
21222324252627
282930    

文章目录

推荐关键字: Linux webpack js 算法 MongoDB laravel JAVA jquery javase redis