# nodejs模块学习: express 解析
# nodejs模块学习: express 解析
nodejs 发展很快,从 npm 上面的包托管数量就可以看出来。不过从另一方面来看,也是反映了 nodejs 的基础不稳固,需要开发者创造大量的轮子来解决现实的问题。
知其然,并知其所以然这是程序员的天性。所以把常用的模块拿出来看看,看看高手怎么写的,学习其想法,让自己的技术能更近一步。
## 引言
[前面一篇文章](http://www.cnblogs.com/htoooth/p/7116480.html)都已经研究过 express 3.x 中的核心框架 connect 的代码,这一阵来看看 express 3.x 和 express 4.x 的核心代码是怎样,特别关注两个部分:
1. express 3.x 中 connect 的使用。
2. express 4.x 中 的流程
## 解析
要使用 express 首先要明白几个概念:
* application (3.x)
* subApplication (3.x)
* router (3.x)
* middleware (3.x)
* route (3.x)
* layer (4.x)
首先 application 就是指的一个 express 实例,这个实例可以有自己环境变量等,而 subApplication 是指的 application 下面又会嵌套 一个 express 实例对像。
每一个 application 中都只有一个 router 路由对像,这个对像管理这个 application 下面有所有的 subApplication, middleware 和 route.
subApplication 不说了, middleware 的表现形式在 express 中有三种: `fn(req, res, next)`,`fn(err, req, res, next)`, `fn(req, res)`。
第一种是正常的中间件:你对数据进行处理,然后调用 next 调用下一个中间件。
而第二种是错识中间件: 你出错口才调用它,而且必须是 四个参数,不能多也不能少。
第三种也是对数据进行处理,但是没有 next 说明数据到里结束。
route 就指的某个具体的路由,就是比如:`get, post, delete, put, all`等方法,这类方法能新建一个路由的对像。
layer 是 4.x 新出来的概念,这个也简单,就是方法的执行体。所谓的执行体,在 3.x中, subApplication 调用 handle, route调用 handle, middleware 调用自己,而在 4.x 中,这些都变成了 layer ,统一起来了。 请求来了, 就遍历 layer 看看哪个 layer匹配了路由,然后执行 layer.handle_request 方法。
上面说了这么多就是说 一个 application 就是由 subApplication, middleware, route 组成的, 这些才是真正的有执行体的地方, 下面说下, 这几个部分怎样加入到一个 application 中去的。
subApplication 加到入到 application 一般是调用 use 方法:
```js
var userCenterApp = express()
var app = express()
app.use('/user', userCenterApp);
// app.use(subApplication);
```
在上面代码中, `/user` 是指的 子应用的 挂载点,可以理解成,这个子应用的所有的方法,都是在 `/user` 这个 url 之下执行的。
如果没有第一个参数, 那是个子应用的挂载点默认是 `/` 可以理解成根应用。
这其中,当子应用挂载成功到父应用上时,子应用会发出一个 mount 事件,express 会在默认情况下,把父应用的 settings, engines 等设置都拷过来。
```js
this.on('mount', function onmount(parent) {
// inherit trust proxy
if (this.settings[trustProxyDefaultSymbol] === true
&& typeof parent.settings['trust proxy fn'] === 'function') {
delete this.settings['trust proxy'];
delete this.settings['trust proxy fn'];
}
// inherit protos
// 复制属性到 子 express 的 request 和 response 中去
setPrototypeOf(this.request, parent.request)
setPrototypeOf(this.response, parent.response)
setPrototypeOf(this.engines, parent.engines)
setPrototypeOf(this.settings, parent.settings)
});
```
如果你需要子应用与父应用有不同的设置,可以监听这个事件,可以这样做:
```js
var subApp = express();
subApp.on('mount', function(parent) {
// dosomething
delete parent.locals.settings; // 不继承父 App 的设置
Object.assign(app.locals, parent.locals);
})
```
通常我们都不对 subApplication 进行设置,直接把父级 locals 变量直接合并过来。
middleWare 加入到 application 中也是使用 use 方法:
```js
var app = express();
app.use(function(req, res, next) {
//正常使用
})
app.use(function(next, req, res, next) {
//错误处理
})
app.use(function(req, res) {
// 到此为止
})
app.use('/user', function(req, res, next) {
//针对 /user 下的所有请求都调用此处方法
})
```
一般来说 错误处理中间件都会放在所有的中间件的最后面,这会有两个处理中间件,一个是针对客户端不可知路由,一个是针对服务器错误。
```js
app.all('*', function clientError(req, res, next) {
res.rend('404');
})
app.use(function serverError(err, req, res, next) {
res.rend('404);
})
```
router 加入到 application 中也是使用 use 方法:
```js
var router = express.Router()
var app = express();
router.get('/app', fn);
app.use(router); // 挂载到 根目录 / 下
app.use('/user', router); // 挂载到 /user 下
```
route 加入到 application 中有两种方法:app, router
// 通过 app 使用
```js
// 这样使用
app.get('/user', fn1, fn2)
app.post('/user', fn3, fn4)
app.delet('/user', fn5, fn6)
// 也可以这样使用
var route = app.route('/user');
route.get(fn1, fn2)
.post(fn3, fn4)
.delete(fn5, fn6)
```
在 router 中,也可以这样使用:
```js
var router = express.Router()
router.get('/user', fn1, fn2)
router.post('/user', fn3, fn4)
// 另一种写法
var route = router.route('/user')
route.get(fn1, fn2)
.post(fn3, fn4)
```
前面 route 中的 `fn*`,其实也是中间件,遵守中间件的使用方法。
而 express 中核心的路由循环,就在 _router.handle 中,如下:
```js
proto.handle = function handle(req, res, out) {
var self = this;
// some thing
next();
function next(err) {
var layer;
var match;
var route;
// 在这里每次请求会遍历所有下一个路由,直到 match 成功后,会跳出循环
while (match !== true && idx app.handle ===> this._router.handle ===> if layer.match(path) ==> layer.handle_request`
而 app.use 方法就是调用 _router.use 方法:
```js
// app.use
fns.forEach(function (fn) {
// non-express app
if (!fn || !fn.handle || !fn.set) { // 如果只是一个函数,直接加入到 route 中
return router.use(path, fn);
}
// 下面针对的是一个子 express 的应用,就是指有 handle 的应用
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);
});
});
```
router.use 方法
```js
proto.use = function use(fn) {
var offset = 0;
var path = '/';
var callbacks = flatten(slice.call(arguments, offset));
for (var i = 0; i
# nodejs模块学习: express 解析的更多相关文章
- nodejs模块学习: webpack
nodejs模块学习: webpack nodejs 发展很快,从 npm 上面的包托管数量就可以看出来.不过从另一方面来看,也是反映了 nodejs 的基础不稳固,需要开发者创造大量的轮子来解决现实 ...
- nodejs模块学习: connect解析
nodejs模块学习: connect解析 nodejs 发展很快,从 npm 上面的包托管数量就可以看出来.不过从另一方面来看,也是反映了 nodejs 的基础不稳固,需要开发者创造大量的轮子来解决 ...
- nodejs模块学习: connect2解析
nodejs模块学习: connect2 解析 nodejs 发展很快,从 npm 上面的包托管数量就可以看出来.不过从另一方面来看,也是反映了 nodejs 的基础不稳固,需要开发者创造大量的轮子来 ...
- nodejs模块学习: express-session 解析
nodejs模块学习: express-session 解析 nodejs 发展很快,从 npm 上面的包托管数量就可以看出来.不过从另一方面来看,也是反映了 nodejs 的基础不稳固,需要开发者创 ...
- python模块学习---HTMLParser(解析HTML文档元素)
HTMLParser是Python自带的模块,使用简单,能够很容易的实现HTML文件的分析. 本文主要简单讲一下HTMLParser的用法. 使用时需要定义一个从类HTMLParser继承的类,重定义 ...
- nodejs 实践:express 最佳实践(七) 改造模块 connect2 解析
nodejs 实践:express 最佳实践(七) 改造模块 connect2 解析 nodejs 发展很快,从 npm 上面的包托管数量就可以看出来.不过从另一方面来看,也是反映了 nodejs 的 ...
- nodejs 实践:express 最佳实践(四) express-session 解析
nodejs 实践:express 最佳实践(四) express-session 解析 nodejs 发展很快,从 npm 上面的包托管数量就可以看出来.不过从另一方面来看,也是反映了 nodejs ...
- nodejs 实践:express 最佳实践(三) express 解析
nodejs 实践:express 最佳实践(三) express 解析 nodejs 发展很快,从 npm 上面的包托管数量就可以看出来.不过从另一方面来看,也是反映了 nodejs 的基础不稳固, ...
- nodejs 实践:express 最佳实践(五) connect解析
nodejs 实践:express 最佳实践(五) connect解析 nodejs 发展很快,从 npm 上面的包托管数量就可以看出来.不过从另一方面来看,也是反映了 nodejs 的基础不稳固,需 ...
随机推荐
- EntityFramework6.X之概述
实体框架(EF6.X)是一种对象/关系映射器(O/R Mapping解决方案),一套支持开发面向数据的软件应用技术,采用特定域对象和关系数据形式使用数据,而不必考虑存储这些数据的基础数据库表和列,上层 ...
- 客户端存储 API
介绍两个在客户端存储数据的 API localStorage与sessionStorage 两个都是window对象的属性,利用这两个属性,可以在客户端存储一些数据 相比cookie,这种存储方式的优 ...
- linux压缩及vi操作
一:Linux的压缩方式 1.tar cvf 对文件进行压缩,tar cvf+压缩文件完成的命名+需要压缩的文件 2,tar -tf +命名的压缩文件:表示查看目录里面的内容 3,tar -xf 解压 ...
- Spring+SpringMVC+MyBatis深入学习及搭建(五)——动态sql
转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6908763.html 前面有讲到Spring+SpringMVC+MyBatis深入学习及搭建(四)——My ...
- [asp.net mvc 奇淫巧技] 04 - 你真的会用Action的模型绑定吗?
在QQ群或者一些程序的交流平台,经常会有人问:我怎么传一个数组在Action中接收.我传的数组为什么Action的model中接收不到.或者我在ajax的data中设置了一些数组,为什么后台还是接收不 ...
- webpack务虚扫盲
打包工具的角色 所谓打包工具在web开发中主要解决的问题是: (1)文件依赖管理.毕竟现在都是模块化开发,打包工具首先就是要梳理文件之间的依赖关系. (2)资源加载管理.web本质就是html.js和 ...
- Ionic3新特性--页面懒加载1
Ionic3新的懒加载机制给我带来了如下新特性: 避免在每一个使用到某Page的Module或其他Page中重复的import这个类(需要写一堆路径) 允许我们通过字符串key在任何想使用的地方获取某 ...
- Android WebView 不支持 H5 input type="file" 解决方法
最近因为赶项目进度,因此将本来要用原生控件实现的界面,自己做了H5并嵌入webview中.发现点击H5中 标签 不能打开android资源管理器. 通过网络搜索发现是因为 android webvie ...
- css 中的背景图片小技巧和存在的坑
body 的背景图设置 第一种 :这种情况下背景图片可以缩放 但是不能完全等比缩放 background: url(imgs/1.jpg)no-repeat; background-position: ...
- python3 简单实现从csv文件中读取内容,并对内容进行分类统计
新手python刚刚上路,在实际工作中遇到如题所示的问题,尝试使用python3简单实现如下,欢迎高手前来优化import csv #打开文件,用with打开可以不用去特意关闭file了,python ...