美文网首页
Express源码书写

Express源码书写

作者: 强某某 | 来源:发表于2020-08-31 16:00 被阅读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 具体路由处理
    路由模块实现图例.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() {
        
    })//甚至更多*/
    
    • 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;
    

    相关文章

      网友评论

          本文标题:Express源码书写

          本文链接:https://www.haomeiwen.com/subject/qsausktx.html