Node express框架源码解析

1.express框架中一些核心的文件

  1. middleware
    init.js:初始化request,response
    query.js:格式化url,将 url 中的 rquest 参数剥离,储存到 req.query中

  2. router:
    index.js:负责中间件的插入和链式执行
    route.js:主要是来处理路由信息的,每条路由都会生成一个Route实例
    layer.js:Layer存储了每个路由的 path 和 handle 等信息,并且实现了 match 和 handle 的功能

  3. express.js application.js:是主要的框架文件,暴露了 express 的 api

  4. request.js response.js:提供了一些方法丰富request和response实例的功能

  5. view.js封装了模板渲染引擎,通过 res.render() 调用引擎渲染网

2.express启动

  1. app.handle():负责路由逻辑的主要函数
  2. mixin(): 为目标对象添加源对象中不同名的属性,并返回目标对象
    第一个参数:目标对象
    第二个参数:源对象
    第三个参数:默认为 true,为 true 时会将同名的属性用原对象的属性重定义
  3. 设置 app 的 request 和 response 对象,继承顶部引入的 req,res.
  4. app.init():初始化和默认配置
var Route = require('./router/route');
var Router = require('./router');
exports = module.exports = createApplication;  // app=exports
exports.application = proto; // app.{} 给app添加方法
exports.Route = Route;       // app.Route
exports.Router = Router;     // app.Router
function createApplication() {
    var app = function(req, res, next) { 
        app.handle(req, res, next); 
    };
    mixin(app, EventEmitter.prototype, false);
    mixin(app, proto, false);
    app.request = Object.create(req, {
        app: { configurable: true, enumerable: true, writable: true, value: app }
    })
    app.response = Object.create(res, {
        app: { configurable: true, enumerable: true, writable: true, value: app }
    })
    app.init();
    return app;
}

3.layer.js

原因:如果路由很多,stack 数组会不断增大,匹配效率下降,为了提高效率,就出现了 layer
作用:

  1. 将同一个 path 形成一层,并提供 match 方法进行匹配判断,handle 方法来执行当前层的深入匹配 methods
  2. 每当一个路由注册的时候,就会生成一个 Layer 实例

layer:

  1. handle:route 在创建 Layer 对象时传入的中间件函数
  2. params:req.params
  3. path:定义路由时传入的 path,默认是’/’
  4. regexp:用于进行路由匹配,它的值是由 pathRegexp 得来的,它的功能是将 path 转换成 regexp
// router/layer.js 文件
function Layer(path, options, fn) {
  if (!(this instanceof Layer)) {
    return new Layer(path, options, fn);
  }
  debug('new %o', path)
  var opts = options || {};
  this.handle = fn;
  this.name = fn.name || '<anonymous>';
  this.params = undefined;
  this.path = undefined;
  this.regexp = pathRegexp(path, this.keys = [], opts);
  this.regexp.fast_star = path === '*'
  this.regexp.fast_slash = path === '/' && opts.end === false
}

4.Router.js

router:

  1. proto 返回的 router 函数就是 app.Router() 方法
  2. router 中的 stack 数组存放 子路由 或者 中间件
  3. router.handle 方法,用于循环 stack 数组,匹配路径执行响应回调函数
  4. route.route 方法中实例化一个 Route 对象,还实例化一个 Layer 对象,然后将 Route 对象赋值给 layer.route,最后将这个 Layer 添加到 stack 数组中,就是用 Layer 对象存储了路由的相关信息,再存入 stack 数组
// router/index.js 中可以找到  
// proto = router
var proto = module.exports = function(options) {
    var opts = options || {};
    function router(req, res, next) {
        router.handle(req, res, next);
    }
    setPrototypeOf(router, proto)
    router.params = {};
    router._params = [];
    router.caseSensitive = opts.caseSensitive;
    router.mergeParams = opts.mergeParams;
    router.strict = opts.strict;
    router.stack = [];
    return router;
};

// router.route
proto.route = function route(path) {
    // new了一个 Route 对象
    var route = new Route(path);
    // new了一个 Layer 对象
    var layer = new Layer(path, {
        sensitive: this.caseSensitive,
        strict: this.strict,
        end: true
    }, route.dispatch.bind(route));
    // 将 Route 对象赋值给 layer.route
    layer.route = route;
    // 最后将这个 Layer 添加到 stack 数组中
    this.stack.push(layer);
    return route;
};

// router.handle
proto.handle = function handle(req, res, out) {
    var self = this;
    var idx = 0;
    var stack = self.stack;
    function next(err) {
        var layer;
        var match;
        var route;
        while (match !== true && idx < stack.length) {
            layer = stack[idx++];
            match = matchLayer(layer, path);
            route = layer.route;
        }
    }
    if (err) {
        layer.handle_error(err, req, res, next);
    } else {
	    layer.handle_request(req, res, next);
    }
}

5.Route.js

  1. Route 中的 dipatch 方法就是传入 Layer 的 handle 方法
  2. 当 Layer 的路径已经匹配成功,就会交给 Route 来匹配 method
  3. 在 Route 中也会维护一个 stack 数组,用于存储 layer 对象,但该 leyer 对象存储注册的方法名,用于快速匹配是否有注册该方法,在 Route 里面又复用了 Layer,去执行真正回调处理函数。实现高效率匹配
// router/route.js 中可以找到
function Route(path) {
    this.path = path;
  	this.stack = [];
  	debug('new %o', path)
  	this.methods = {};
}

Route.prototype.dispatch = function dispatch(req, res, done) {
    var idx = 0;
    var stack = this.stack;
    req.route = this;
    next();
    function next(err) {
        var layer = stack[idx++];
        if (!layer) {
            return done(err);
        }
        if (layer.method && layer.method !== method) {
            return next(err);
        }
        if (err) {
            layer.handle_error(err, req, res, next);
        } else {
            layer.handle_request(req, res, next);
        }
    }
};

// app.get 的出处
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;
            this.methods[method] = true;
            this.stack.push(layer);
        }
        return this;
    };
});

6.handle

  1. handle 函数就是用来循环匹配请求路径,然后调用对应的回调函数
  2. handle 中有一个 next 方法,就是用来循环 stack 数组的
app.handle = function handle(req, res, callback) {
    var router = this._router;
    // done 就是回调函数
    var done = callback || finalhandler(req, res, {
        env: this.get('env'),
        onerror: logerror.bind(this)
    });
    if (!router) {
        debug('no routes defined on app');
        // 如果不是路由中间件,就直接调用
        done();
        return;
    }
    router.handle(req, res, done);
};


// router.handle
proto.handle = function handle(req, res, out) {
    var self = this;
    var idx = 0;
    var stack = self.stack;
    // done 就是回调函数
    var done = restore(out, req, 'baseUrl', 'next', 'params');
    req.next = next;
    next();
    function next(err) {
        // 省略...
        var layer;
        var match;
        var route;
        while (match !== true && idx < stack.length) {
            layer = stack[idx++];
            // 通过 layer 对象的 match 方法匹配路由
            match = matchLayer(layer, path);
            route = layer.route;
            var method = req.method;
            var has_method = route._handles_method(method);
        }
    }
};

7.methods

const express = require("express");
var app = express();
var router = express.Router();
// app 使用 get 方法
app.get("/",(req,res,next)=>{ res.send("<h1>首页1</h1>") });    // '/' 路由
// 路由使用 get 方法
router.get("/",(req,res,next)=>{ res.send("<h1>首页1</h1>") });  // '/route' 路由
app.use("/route",router);
app.listen(3000,()=>{console.log('http://localhost:3000')})
// app 使用 methods 方法
methods.forEach(function(method){
    app[method] = function(path){
        if (method === 'get' && arguments.length === 1) {
            return this.set(path);
        }
        this.lazyrouter();
        var route = this._router.route(path);
        // 调用 route 中的 methods 方法
        route[method].apply(route, slice.call(arguments, 1));
        return this;
    };
});

// 路由使用 methods 方法
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 对象把方法也存储起来了
            layer.method = method;
            this.methods[method] = true;
            this.stack.push(layer);
        }
        return this;
    };
});
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Node-RED 是一个基于 Node.js 构建的开源流程编排工具,它具有简单易用的特点,可以帮助用户快速搭建、管理和部署流程应用。Node-RED 的源码分析是指对其代码库进行深入剖析,以便了解其内部结构和工作原理。 首先,Node-RED 的源码主要由 JavaScript 编写,大部分代码都是围绕 Node.js 运行时环境展开。其核心功能是基于事件驱动的管道式数据处理,通过流程图的方式将各种节点(Node)串联起来,形成一个数据处理流程。因此,在源码分析中,需要重点关注事件驱动机制、节点的定义与扩展、消息传递机制等方面的实现细节。 其次,Node-RED 采用了 Express 框架来搭建 Web 服务,并通过 WebSocket 实现了实时通信。在源码分析中,需要深入了解其 Web 服务的实现方式,以及与客户端的交互方式和消息传递机制。此外,还需要对其对外部插件和节点的支持机制进行分析,以便了解其扩展性和定制化能力。 最后,在源码分析中还需要深入了解 Node-RED 的核心模块以及各种节点的实现方式,比如文件操作、网络请求、数据库操作等,以及其对于不同数据格式的处理能力。同时,还需要关注其错误处理、安全机制、性能优化等方面的实现方式。 总的来说,Node-RED 的源码分析是一个复杂而全面的工作,需要对 JavaScript 和 Node.js 相关技术有深入的了解,以便更好地理解其内部结构和工作原理,为进一步的定制和扩展工作提供有力的支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值