Express框架中_router 对象数据结构使用详解
作者:乔治_x
_router 对象介绍
_router 对象是一个私有的属性,但是它保存了重要的内容。其中就包括所有的合理的 Layer 对象以及其他的。它对我们分析 express 运行脉络有很重要的作用。
使用调试直观的获取 _router 对象
要调试就需要准备以下的内容:
- 一个简单的能运行的 express demo。
- 使用 vscode 编辑器进行调试。
- 在合适的地方打上断点并启动带有断点的服务。
- 观察全部属性并找到 _router 对象以及属性。
- 找出 stack.layer 的排列顺序得出
- 分析 _router 的嵌套对象
一个简单的可以运行的 demo
- index.js 是程序的入口文件:
import express from 'express' const app = express() app.get('/', (_, res)=> { res.json({ ab: 123, }) }) app.get('/abc', () => { res.send('x') }) app.listen(3232, () => { console.log("Listen on port 3232") })
app 上使用了 get 方法添加了两个路由:
- /: 响应一个 json
- /abc: 响应一个字符串
然后监听在 3232 端口。
使用 vscode 初始化一个调试文件
{ "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "Launch Program", "skipFiles": [ "<node_internals>/**" ], "program": "${workspaceFolder}/index.js" } ] }
调试文件会以 ${workspaceFolder}/index.js 为入口启动 express 服务,在 vscode 侧边栏找到调试项目,开始调试。
在合适的地点打一个断点
哪里合适?
当然是在 listen 之前最为合适,此时 express 内部工作已经基本完成,只需要监听端口等待请求到来。
启动服务
使用 vscode 调试配置与调试工具启动服务
观察全部属性并找到 _router 对象以及属性
- 观察全部属性
由于 express 创建 app, 混入了很多的属性和方法所以需要对其进行有基本的认识,其中被本文关注的 _router 是需要重点关注的
- _router对象
_router 对象属性比较简单,其中需要关注的就是 stack 属性,因为 stack 属性后期会被遍历取出 layer 来获取 handle 执行函数。
找出 stack.layer 的排列顺序
layer 的顺序如下:
- query: 内置 query 中间件产生
- expressInit: 内置 exressInit 中间件产生
- boundDispatch 1: get 路由 / 产生
- boundDispatch 2: get 路由 /abc 产生
- _router 的 stack 对象存储的 Layer 实例,不是 route 对象的不添加 layer.route 为空:
以 query 内置中间件为例, route 是 undefined,表示不是路由,反之。
- 手动添加的路由 router 对象,保存了自己的 route 信息
- path
- stack
- methods
- ...
route 中保存了自己的 stack 属性,stack 保存属于本路由的 layer, 暂且称之为 routeLayer, routeLayer 实例中保持了 handle 方法,这个方法就是我们在 express 中写的最多的路由处理函数。
分析 _router 的嵌套对象
_router 对象是 app 对象的属性,是整个对象的路由属性。
- _router.stack 存储了所有的 RouterLayer 对象,stack 中 RouterLayer 有自己的添加顺序,内置 query/expressInit 中间件被首先添加,然后是手动添加的路由和中间件。
- RouterLayer 中存在 route 属性,纯中间件 route 属性为未定义,路由相关中间件 route 指向 Route 实例对象。app -> _router -> stack -> routerLayer[] -> routerLayer/routerLayer.route -> route.stack -> routeLayer[]/-> routeLayer/routeLayer.handle。
_router 对象的相关源码
app.lazyrouter = function lazyrouter() { if (!this._router) { this._router = new Router({ caseSensitive: this.enabled('case sensitive routing'), strict: this.enabled('strict routing') }); this._router.use(query(this.get('query parser fn'))); this._router.use(middleware.init(this)); } };
_router 函数在 lazyrouter 函数中被实现,并且是懒实现(有了就不在重新实现了),实现之后立即添加两个中间件。本质就是一个 Router 类的实例。
_router 的数据结构分析
- _router 不是顶层数据,app 才是。
- _router 中保存了 router 相关的重要数据。stack 属性中保存所有的 routerlayer 层级数据。
- _router.stack 的每一个 layer 中 route 属性一个有效路由数据。
- route 中也保存了 stack route 层的 layer 数据。
- route 层级的 layer 保存了 handle 和 handle 的参数
- _router 数据结构中嵌套两个栈一个是 router.stack 栈,一个是 route.stack 栈,保存自己层级的数据。
- handle 作为底层路由的处理函数,被保存在 route.stack 的 layer 中
_router 中包含 router 和 route 层,每一层都自己的 stack, router 层与 route 层通过 stack 的 layer 中的 route 属性链接,route 与 handle 函数链接通过 route 中 stack 储存的 layer 的 handle 属性链接,这就构成一个 _router 的数据结构:
小结
本文主要关注 _router 对象,以及其 vscode 调试方法,属性层级和对应数据结构。熟悉其内部源码实现。
以上就是Express框架中_router 对象数据结构使用详解的详细内容,更多关于Express _router 对象数据结构的资料请关注脚本之家其它相关文章!