博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
探讨Express Router & Route
阅读量:6489 次
发布时间:2019-06-24

本文共 5318 字,大约阅读时间需要 17 分钟。

Express

基于 Node.js 平台,快速、开放、极简的 web 开发框架

安装

//应用生成器工具npm install express-generator -g//创建express应用包express app//安装依赖npm install

成功生成后,会产生以下的目录和文件:

|---bin|---node_module|---public|---routes|---view|---app.js|---package.json

接下来我们通过:

npm start

启动程序后,访问127.0.0.1:3000,就能访问到express的页面了。

接下来通过研究源码,来探讨express路由原理的实现。

路由

我们通过查看app.jsindex.js文件:

app.js

var index = require('./routes/index');app.use('/', index);//或app.get('/', index);

routes/index.js

var express = require('express');var router = express.Router();router.get('/', function(req, res, next) {  res.render('index', { title: 'Express' });});

可以看出,express的路由大概实现 定义一份路由规则文件,再通过app.use()或者app[METHOD]来建立路由规则访问联系,虽然两者的结果一样,但是存在本质上的区别。

下图是主要涉及的几个文件:

图片描述

接下来我们通过源码首先看看app.use()具体是一个什么样实现思路。

app.use

我们打开node_module里的express文件夹。打开lib/application.js文件。

app.use = function use(fn) {    var offset = 0;    var path = '/';    // default path to '/'    // disambiguate app.use([fn])    if (typeof fn !== 'function') {        var arg = fn;        while (Array.isArray(arg) && arg.length !== 0) {            arg = arg[0];        }        // first arg is the path        if (typeof arg !== 'function') {            offset = 1;            path = fn;        }    }    var fns = flatten(slice.call(arguments, offset));    if (fns.length === 0) {        throw new TypeError('app.use() requires middleware functions');    }    // setup router    this.lazyrouter();    var router = this._router;    fns.forEach(function(fn) {        // non-express app        if (!fn || !fn.handle || !fn.set) {            return router.use(path, fn);        }        debug('.use app under %s', path);        fn.mountpath = path;        fn.parent = this;        // restore .app property on req and res        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);            });        });        // mounted an app        fn.emit('mount', this);    }, this);    return this;};

看到use里部分的代码,开始做了判断处理use挂载的是路径还是function,并且通过lazyrouter()方法实例router类,并且全局只存在一个router实例对象,最终调用router.use()方法。

接着,我们到lib/router/index.js 看router.use方法的实现:

proto.use = function use(fn) {  var offset = 0;  var path = '/';  // default path to '/'  // disambiguate router.use([fn])  if (typeof fn !== 'function') {    var arg = fn;    while (Array.isArray(arg) && arg.length !== 0) {      arg = arg[0];    }    // first arg is the path    if (typeof arg !== 'function') {      offset = 1;      path = fn;    }  }  var callbacks = flatten(slice.call(arguments, offset));  if (callbacks.length === 0) {    throw new TypeError('Router.use() requires middleware functions');  }  for (var i = 0; i < callbacks.length; i++) {    var fn = callbacks[i];    if (typeof fn !== 'function') {      throw new TypeError('Router.use() requires middleware function but got a ' + gettype(fn));    }    // add the middleware    debug('use %o %s', path, fn.name || '
') var layer = new Layer(path, { sensitive: this.caseSensitive, strict: false, end: false }, fn); layer.route = undefined; this.stack.push(layer); } return this;};

通过对比app.use方法,router.use前半部分处理相同,但后面实例化一个Layer类,并且丢进stack里。

Layer类保存Router和Route一些数据信息:

图片描述

相同点:

path都是存放挂载路径,options.end用来判断是否是路由中间件。

不同点:

Router和Route的区别是一个是添非路由中间件,另一个是添加路由中间件。

他们的layer.route指向也不一样,一个指向undefined,另一个没有route属性。

文章进行到一半,我们小总结一下,app.use()方法是用来添加非路由中间件的,最终是调用router实例方法,会实例划一个Layer类对象用于存放数据,并且把layer对象push进router.stack里,全局只有一个router。

app[METHOD]

我们通过源码去探讨路由中间件app[METHOD]是一个怎样的原理:

app.jsapp.use('\',index)改成app.get('\',index).

application.js:

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;    };});

可以看出,代码里做了一个app.get方法的判断处理,get方法只有一个参数时,是获取app的本地变量,后面还是实例化router对象,并且用router上的route方法放回的对象去调用Route类上的route[METHOD].

/lib/router/route.js

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;  };});

在route里有一个实例化的layer,且放在stack里,与Router的layer不同的是,Route的没有layer.route且layer.method存放http方法。

到这里,我们大概可以总结下路由中间件和非路由中间件的联系,如下图:

图片描述

app初始化时,会push两个方法(init,query)进router.stack里。我们可以通过app.use往app添加非路由中间件,也可以通过app[METHOD]添加路由中间件,同样是push layer实例对象,但route是指向Route实例化的对象。

完整的Router逻辑过程,如图:

图片描述

总结

  1. express中添加中间件方法有app.use和app[METHOD],当然还有内置的Router类,app.use用来添加非路由中间件,app[METHOD]用来添加路由中间件。
  2. Layer类封装中间的path和handle(fns的处理)
  3. Router和Route都有对应的stack,但是Route在整个app中只有一个,而Route可以又多个。放在Router
    stack里的路由中间件,通过Layer.route指向Route,与Route stack相关联起来

转载地址:http://kwouo.baihongyu.com/

你可能感兴趣的文章
使用Jquery From插件实现异步上传文件的功能
查看>>
互联网公司logo logo制作
查看>>
一台WAF价格大约是多少?想买WAF的先来了解下。
查看>>
滚涂机——有哪些分类以及特点
查看>>
加一度教你如何做好百度竞价数据分析
查看>>
C语言之printf函数
查看>>
区块链将会取代支付宝?区块链支付有什么原理?
查看>>
docker swarm笔记
查看>>
[转载]Outlook Express压缩计数器清零
查看>>
一步步构建基于DRBD的数据共享
查看>>
java中的 i++ 和++i
查看>>
Lua和C++交互详细总结
查看>>
LoadRunner对不同协议的选择
查看>>
python定时任务
查看>>
再次开通BLOG
查看>>
Neutron 如何支持多种 network provider - 每天5分钟玩转 OpenStack(70)
查看>>
我的友情链接
查看>>
Mysql 优化点滴
查看>>
Objective-C中的字符串比较
查看>>
Advanced Format (AF) 硬盘来了,你准备好了吗?
查看>>