We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
继上一次的koa源码解读,今天来聊一下koa-router。
koa
koa-router
从源码文件中可以看到,koa-router只有两个文件,layer.js和router.js,分别定义了Router和Layer两个对象。相对于express的内置路由,koa-router少了一个route对象,使得逻辑更加简单,下面通过一张图来解释Router和Layer对象之间的关系。
layer.js
router.js
Router
Layer
express
route
正如上图显示,Router对象中有一个stack的成员属性,而stack又是一个由Layer组成的数组,这样就使两者关联起来了。两个对象之间的原型函数函数也列举出来了,比较直观和简单。
stack
在我们的项目路由文件中引入koa-router的时候,如下:
const router = require('koa-router')()
然后执行定义如下路由的时候:
router.get('/', async (ctx, next) => { await ctx.render('users/index',{ title:'用户中心' }) })
其实首先调用的是router.js/routes这个入口函数。下面为routes函数的源码:
router.js/routes
routes
Router.prototype.routes = Router.prototype.middleware = function () { var router = this; var dispatch = function dispatch(ctx, next) { debug('%s %s', ctx.method, ctx.path); var path = router.opts.routerPath || ctx.routerPath || ctx.path; var matched = router.match(path, ctx.method); //通过path=users/index和method=GET来判断匹配 var layerChain, layer, i; if (ctx.matched) { ctx.matched.push.apply(ctx.matched, matched.path); } else { ctx.matched = matched.path; } ctx.router = router; if (!matched.route) return next(); var matchedLayers = matched.pathAndMethod var mostSpecificLayer = matchedLayers[matchedLayers.length - 1] ctx._matchedRoute = mostSpecificLayer.path; if (mostSpecificLayer.name) { ctx._matchedRouteName = mostSpecificLayer.name; } layerChain = matchedLayers.reduce(function(memo, layer) { memo.push(function(ctx, next) { ctx.captures = layer.captures(path, ctx.captures); ctx.params = layer.params(path, ctx.captures, ctx.params); return next(); }); return memo.concat(layer.stack); }, []); return compose(layerChain)(ctx, next); }; dispatch.router = this; return dispatch; };
其中定义了一个dispatch函数用来处理路由。其中this对象为当前文件定义的路由对象。例如w我的项目文件中定义了一个路由的user.js文件,并且内容如下:
dispatch
this
user.js
const router = require('koa-router')() router.prefix('/users') router.get('/', async (ctx, next) => { await ctx.render('users/index',{ title:'用户中心' }) }) router.get('/register/:id', async (ctx, next) => { await ctx.render('users/register',{ title:'用户注册' }) }) router.get('/login',async (ctx,next) => { await ctx.render('users/login',{ title:'用户登录' }) }) router.get('/setting',async (ctx,next) => { await ctx.render('users/setting',{ title: '用户设置' }) }) module.exports = router
那么这个router对象结构如下:
router
其中method为Router对象构造函数中定义的:
method
this.methods = this.opts.methods || [ 'HEAD', 'OPTIONS', 'GET', 'PUT', 'PATCH', 'POST', 'DELETE' ];
stack数组就是user.js中定义的四个Layer,接下来就是进行遍历并且匹配。找到可以匹配当前path和method的layer并且放到layerChain中。最后将这个layerChain交由compose去处理,也就是遍历中间件。 值得一提的是,在调用compose之前,会首先给matchedLayers添加一个中间件,具体代码如下:
path
layer
layerChain
compose
matchedLayers
layerChain = matchedLayers.reduce(function(memo, layer) { memo.push(function(ctx, next) { ctx.captures = layer.captures(path, ctx.captures); ctx.params = layer.params(path, ctx.captures, ctx.params); return next(); }); return memo.concat(layer.stack); }, []);
这一段代码主要是给先给中间件数组添加一个中间件函数,用于获取路由中的参数并且赋值给ctx中的params。此时传入compose的middleware数组结构如下:
ctx
params
middleware
其中function(ctx,next)就是上面我们push进去的函数,然后下面的async (ctx,next) =>{}就是我们传入的中间件函数。
function(ctx,next)
push
async (ctx,next) =>{}
例如,对于下面这种情况:
router.get('/register/:id', async (ctx, next) => { await ctx.render('users/register',{ title:'用户注册' }) })
如我们访问127.0.0.1:3001/users/register/id=123就可以得出ctx.params为{id:123}的这个参数对象。 所以koa中首先会给每个路由添加一个获取路由参数的中间件,然后依次处理后面自定义的中间件。
127.0.0.1:3001/users/register/id=123
ctx.params
总结一下,正常的路由流程大概是如下图所示:
代码如下:
Router.prototype.register = function (path, methods, middleware, opts) { opts = opts || {}; var router = this; var stack = this.stack; // support array of paths if (Array.isArray(path)) { path.forEach(function (p) { router.register.call(router, p, methods, middleware, opts); }); return this; } // create route var route = new Layer(path, methods, middleware, { end: opts.end === false ? opts.end : true, name: opts.name, sensitive: opts.sensitive || this.opts.sensitive || false, strict: opts.strict || this.opts.strict || false, prefix: opts.prefix || this.opts.prefix || "", ignoreCaptures: opts.ignoreCaptures }); if (this.opts.prefix) { route.setPrefix(this.opts.prefix); } // add parameter middleware Object.keys(this.params).forEach(function (param) { route.param(param, this.params[param]); }, this); stack.push(route); return route; };
可以看到,这个函数还是比较简单的,首先是判断传进来的path是否是数组,否则就递归执行自身,直到数组中的每一个值都完成注册为止。否则就创建一个Layer对象push进当前router对象的 stack数组中。从而完成注册功能。
对于router中的其他原型函数,就不再具体分析了 上面只是个人的一些探索,若有不对的地方,欢迎私聊拍砖。
The text was updated successfully, but these errors were encountered:
eggjs/egg#882
Sorry, something went wrong.
No branches or pull requests
继上一次的
koa
源码解读,今天来聊一下koa-router
。文件结构及内容关联
从源码文件中可以看到,
koa-router
只有两个文件,layer.js
和router.js
,分别定义了Router
和Layer
两个对象。相对于express
的内置路由,koa-router
少了一个route
对象,使得逻辑更加简单,下面通过一张图来解释Router
和Layer
对象之间的关系。正如上图显示,
Router
对象中有一个stack
的成员属性,而stack
又是一个由Layer
组成的数组,这样就使两者关联起来了。两个对象之间的原型函数函数也列举出来了,比较直观和简单。运行流程
在我们的项目路由文件中引入
koa-router
的时候,如下:然后执行定义如下路由的时候:
其实首先调用的是
router.js/routes
这个入口函数。下面为routes
函数的源码:其中定义了一个
dispatch
函数用来处理路由。其中this
对象为当前文件定义的路由对象。例如w我的项目文件中定义了一个路由的user.js
文件,并且内容如下:那么这个
router
对象结构如下:其中
method
为Router
对象构造函数中定义的:stack
数组就是user.js
中定义的四个Layer
,接下来就是进行遍历并且匹配。找到可以匹配当前path
和method
的layer
并且放到layerChain
中。最后将这个layerChain
交由compose
去处理,也就是遍历中间件。值得一提的是,在调用
compose
之前,会首先给matchedLayers
添加一个中间件,具体代码如下:这一段代码主要是给先给中间件数组添加一个中间件函数,用于获取路由中的参数并且赋值给
ctx
中的params
。此时传入compose
的middleware
数组结构如下:其中
function(ctx,next)
就是上面我们push
进去的函数,然后下面的async (ctx,next) =>{}
就是我们传入的中间件函数。例如,对于下面这种情况:
如我们访问
127.0.0.1:3001/users/register/id=123
就可以得出ctx.params
为{id:123}的这个参数对象。所以
koa
中首先会给每个路由添加一个获取路由参数的中间件,然后依次处理后面自定义的中间件。总结一下,正常的路由流程大概是如下图所示:
router.prototype.register()
代码如下:
可以看到,这个函数还是比较简单的,首先是判断传进来的
path
是否是数组,否则就递归执行自身,直到数组中的每一个值都完成注册为止。否则就创建一个Layer
对象push
进当前router
对象的stack
数组中。从而完成注册功能。对于
router
中的其他原型函数,就不再具体分析了上面只是个人的一些探索,若有不对的地方,欢迎私聊拍砖。
The text was updated successfully, but these errors were encountered: