Express源码书写

2020-08-31  本文已影响0人  强某某

Express基本使用

源码结构

路由模块实现图例.png

基本使用

//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() {
    
})//甚至更多*/
const Application=require('./Application');
//creatApplication就是express对象
function creatApplication() {
    //需要将get和listen放到当前应用的实例上,因为是new的
    return new Application();
}
module.exports = creatApplication;
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
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;
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;
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;
上一篇下一篇

猜你喜欢

热点阅读