【序】该文主要是对源码解读过程中遇到的细节困惑进行记录的地方,并且给出解惑的答案,源码解读可以通过连接去查看。
问题一
- 为何要使用
exports = module.exports
代码:
function createApplication() {
return {};
}
exports = module.exports = createApplication;
- 功能:能够直接导出函数,而不需要再引入页面通过对象点属性的方式去调用该函数
- 疑惑:
exports = module.exports = createApplication;
- 解惑:exports是module.exports的一个引用,module.exports对象一旦被赋值,则exports的引用就失效了,因此需要再使用exports = module.exports 使 exports 再次生效,否则module.exports = createApplication 本身就可以生效了
- 引申:createApplication是一个工厂类,所谓的工厂类组要作用是可以根据传入的参数不同而创建出不同类型的实例。
问题二
- 问题 当使用
app.get('/',(res,req)=>{})
就监听了路径为/的路由,是如何做到的? - 关键:调用建立一个数据的对象结构,然后遍历执行
- 在全局设置一个route数组
var router = [{
path: '*',
method: '*',
handle: function(req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('404');
}
}];
- 修改listen函数,将http请求拦截逻辑改为匹配router路由表,如果匹配成功,执行对应的handle函数,否则执行router[0].handle函数。
listen: function(port, cb) {
var server = http.createServer(function(req, res) {
for(var i=1,len=router.length; i<len; i++) {
if((req.url === router[i].path || router[i].path === '*') &&
(req.method === router[i].method || router[i].method === '*')) {
return router[i].handle && router[i].handle(req, res);
}
}
return router[0].handle && router[0].handle(req, res);
});
return server.listen.apply(server, arguments);
}
- 实现get路由请求非常简单,该函数主要是添加get请求路由,只需要将其信息加入到router数组即可
get: function(path, fn) {
router.push({
path: path,
method: 'GET',
handle: fn
});
}
问题三
- 问题 :但此处重写的匹配逻辑过于简单,因为不仅仅要匹配path,同一个path还有不同的method方法,如果简单的使用一个route数组接收所有方法的话,当路由过多时就会导致处理变慢。
- 关键:是使用layer这样的类去替代原本的route数组中的item,使代码逻辑变得清晰。
- 解决:
- 引入layer类来区分同一路径下的方法
function Layer(path, fn) {
this.handle = fn;
this.name = fn.name || '<anonymous>';
this.path = path;
}
//简单处理
Layer.prototype.handle_request = function (req, res) {
var fn = this.handle;
if(fn) {
fn(req, res);
}
};
//简单匹配
Layer.prototype.match = function (path) {
if(path === this.path || path === '*') {
return true;
}
return false;
};
- 将router.stack中的普通对象替换为Layer实例,这样方便在遍历处理router的item时能够让Layer进行识别和执行
var Router = function() {
this.stack = [new Layer('*', function(req, res) {
res.writeHead(200, {
'Content-Type': 'text/plain'
});
res.end('404');
})];
};
Router.prototype.handle = function(req, res) {
var self = this;
for(var i=1,len=self.stack.length; i<len; i++) {
if(self.stack[i].match(req.url)) {
return self.stack[i].handle_request(req, res);
}
}
return self.stack[0].handle_request(req, res);
};
Router.prototype.get = function(path, fn) {
this.stack.push(new Layer(path, fn));
};
问题四
问题三的方法只能够将同一path匹配执行,接下来还需要匹配method方法
- 关键:在Layer中引入Route类,Route类是指在进入Router匹配后的再次路由,在Route类去匹配应该执行的方法
解决:
- 创建Route类
var Route = function(path) {
this.path = path;
this.stack = [];
this.methods = {};
};
Route.prototype._handles_method = function(method) {
var name = method.toLowerCase();
return Boolean(this.methods[name]);
};
Route.prototype.get = function(fn) {
var layer = new Layer('/', fn);
layer.method = 'get';
this.methods['get'] = true;
this.stack.push(layer);
return this;
};
Route.prototype.dispatch = function(req, res) {
var self = this,
method = req.method.toLowerCase();
for(var i=0,len=self.stack.length; i<len; i++) {
if(method === self.stack[i].method) {
return self.stack[i].handle_request(req, res);
}
}
};
- 将Route类整合到Router中
Router.prototype.handle = function(req, res) {
var self = this,
method = req.method;
for(var i=0,len=self.stack.length; i<len; i++) {
if(self.stack[i].match(req.url) &&
self.stack[i].route && self.stack[i].route._handles_method(method)) {
return self.stack[i].handle_request(req, res);
}
}
return self.stack[0].handle_request(req, res);
};
Router.prototype.route = function route(path) {
var route = new Route(path);
var layer = new Layer(path, function(req, res) {
route.dispatch(req, res);
});
layer.route = route;
this.stack.push(layer);
return route;
};
Router.prototype.get = function(path, fn) {
var route = this.route(path);
route.get(fn);
return this;
};