1.express框架中一些核心的文件
-
middleware
init.js:初始化request,response
query.js:格式化url,将 url 中的 rquest 参数剥离,储存到 req.query中 -
router:
index.js:负责中间件的插入和链式执行
route.js:主要是来处理路由信息的,每条路由都会生成一个Route实例
layer.js:Layer存储了每个路由的 path 和 handle 等信息,并且实现了 match 和 handle 的功能 -
express.js application.js:是主要的框架文件,暴露了 express 的 api
-
request.js response.js:提供了一些方法丰富request和response实例的功能
-
view.js封装了模板渲染引擎,通过 res.render() 调用引擎渲染网
2.express启动
- app.handle():负责路由逻辑的主要函数
- mixin(): 为目标对象添加源对象中不同名的属性,并返回目标对象
第一个参数:目标对象
第二个参数:源对象
第三个参数:默认为 true,为 true 时会将同名的属性用原对象的属性重定义 - 设置 app 的 request 和 response 对象,继承顶部引入的 req,res.
- 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
作用:
- 将同一个 path 形成一层,并提供 match 方法进行匹配判断,handle 方法来执行当前层的深入匹配 methods
- 每当一个路由注册的时候,就会生成一个 Layer 实例
layer:
- handle:route 在创建 Layer 对象时传入的中间件函数
- params:req.params
- path:定义路由时传入的 path,默认是’/’
- 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:
- proto 返回的 router 函数就是 app.Router() 方法
- router 中的 stack 数组存放 子路由 或者 中间件
- router.handle 方法,用于循环 stack 数组,匹配路径执行响应回调函数
- 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
- Route 中的 dipatch 方法就是传入 Layer 的 handle 方法
- 当 Layer 的路径已经匹配成功,就会交给 Route 来匹配 method
- 在 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
- handle 函数就是用来循环匹配请求路径,然后调用对应的回调函数
- 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;
};
});