Express源码书写
2020-08-31 本文已影响0人
强某某
Express基本使用
- express是一个函数,koa是类
- express中的中间件可以放置路径,路径的规则和cookie中path一样
- 具体细节是:不写默认就是全匹配,'/'全匹配,'/a'的情况下只要开头匹配就匹配,例如'/a/b'
- express中间件每次只能use一个,不能添加多个参数
- express的路由回调函数一般是一个,但是实际上可以添加N个
- router里面有layer layer(层)里面有route route里面有layer
源码结构
- application 创建应用
- router
- index.js 入口
- layer.js 层
- route.js 具体路由处理
基本使用
//express是一个函数,koa是类
// const app=require('express')();
const app = require('./index')();
//中间件一般放在路由之前
//express中的中间件可以放置路径,这个路劲的规则和cookie中path一样,开始匹配就行
app.use(function(req,res,next) {
next()
});
app.use('/a',function(req,res,next) {
next()
});
/*
不写'/'就是所有路径全匹配
app.use('/',function(req,res,next) {
next()
})
*/
app.get('/', function (req, res, next) {
//没有ctx对象,主要靠的是原生的req和res只不过做了扩展
console.log(1);
// res.end('/')
next();
}, function (req, res, next) {
console.log(11);
next();
}, function (req, res, next) {
console.log(111);
next();
})
app.get('/hello', function (req, res, next) {//没有ctx对象,主要靠的是原生的req和res只不过做了扩展
console.log(2);
res.end('/hello')
})
//处理所有没有匹配的,必须放最下面
// app.all('*',function(req,res) {
// res.end('*')
// })
app.listen(3000, function () {
console.log('listener on 3000');
});
// 源码中之所以pathname和method分开判断是因为
/*
app._router('/').get(function() {
}).post(function() {
})//甚至更多*/
- express.js
const Application=require('./Application');
//creatApplication就是express对象
function creatApplication() {
//需要将get和listen放到当前应用的实例上,因为是new的
return new Application();
}
module.exports = creatApplication;
- application.js
const http = require('http');
const path = require('path');
const Router = require('./router');
const methods = require('methods');
function Application() {
}
//懒加载-延迟加载路由系统-性能优化
Application.prototype.lazy_route = function () {
//避免new Application就创建router,因为可能不用
if (!this._router) {
this._router = new Router();
}
}
Application.prototype.use = function (path, handler) {
this.lazy_route();
this._router.use(path, handler);
}
methods.forEach(method => {
Application.prototype[method] = function (path, ...handlers) {
this.lazy_route();
this._router[method](path, handlers);
}
})
Application.prototype.listen = function () {
let server = http.createServer((req, res) => {
//应用提供一个找不到的方法
function done() {
res.end(`Cannot ${req.method} ${req.url}`)
}
//路由处理不了,才调用done
this._router.handle(req, res, done);
});
server.listen(...arguments);
}
module.exports = Application
- 路由的index.js
const url = require('url');
const Route = require('./route');
const Layer = require('./layer');
const methods = require('methods');
function Router() {
this.stack = [];
}
Router.prototype.use = function (path, handler) {//中间件会放到当前的路由系统中
if (typeof path === 'function') {
handler = path;
path = '/';
}
let layer = new Layer(path, handler);
layer.route = undefined;//如果route是underfined说明是中间件
this.stack.push(layer);
}
//构建一个route
Router.prototype.route = function (path) {
let route = new Route();
let layer = new Layer(path, route.dispatch.bind(route));//给当前调用get方法,放入一层
layer.route = route;
this.stack.push(layer);
return route;
}
methods.forEach(method => {
Router.prototype[method] = function (path, handlers) {//用户调用get方法时,传递了多个处理函数
let route = this.route(path);
route[method](handlers);//交给route,来存储用户真正的handler
}
})
Router.prototype.handle = function (req, res, out) {
//请求到来时,开始处理请求
let { pathname } = url.parse(req.url);
let idx = 0;
let dispatch = () => {//expresss通过next函数来迭代
if (idx === this.stack.length) return out();//如果路由处理不了,交给applicaton处理
let layer = this.stack[idx++];
//路由 中间件 必须要求路径匹配
if (layer.match(pathname)) {//layer有可能是中间件或者路由
if (!layer.route) {//如果是中间件,直接执行对应方法即可
layer.handle_request(req, res, dispatch);
} else {
//路由
//layer.route.methods[req.method.toLowerCase()过滤对应的method,例如只有post,但是get请求来了,没必要继续
if (layer.route.methods[req.method.toLowerCase()]) {
layer.handle_request(req, res, dispatch);
} else {
dispatch();
}
}
} else {
dispatch();
}
}
dispatch();
}
module.exports = Router;
- 路由的layer.js
function Layer(path, handler) {
this.path = path;
this.handler = handler;
}
Layer.prototype.match = function (pathname) {
if (this.path === pathname)return true;
//如果是中间件特殊处理
if (!this.route) {
if (this.path == '/') {
return true;
}
// /a/b
return pathname.startsWith(this.path + '/')
}
}
Layer.prototype.handle_request = function (req, res, out) {
this.handler(req, res, out);
}
module.exports = Layer;
- 路由的route.js
const Layer = require('./layer');
const methods = require('methods');
//每个层都有一个route属性
function Route() {
this.stack = [];
this.methods={};
//表示当前route中有哪些方法{get:true,post:true},可以快速过滤,避免进入不需要进入的dispatch。例如同一个路径,get请求没必要进入post的回调
}
Route.prototype.dispatch = function (req,res,out) {
let idx=0;
let method=req.method.toLowerCase();
let dispatch=()=>{
if (idx===this.stack.length) return out();
let layer=this.stack[idx++];
if (method===layer.method) {//获取内部第一层,是否方法匹配
layer.handle_request(req,res,dispatch);
}else{
dispatch();
}
};
dispatch();
}
methods.forEach(method=>{
Route.prototype[method] = function (handlers) {
handlers.forEach(handler => {
let layer = new Layer('/', handler);
layer.method=method;
this.methods[method]=true;
this.stack.push(layer);
});
}
})
module.exports = Route;