博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
和koa不同的express是怎么实现
阅读量:4086 次
发布时间:2019-05-25

本文共 11138 字,大约阅读时间需要 37 分钟。

koa和express

kao和express都是同一个团队开发的,已经介绍koa的原理,而koa在express的基础上进行了优化:

  • koa使用了类的概念,express没有使用类,而是直接使用函数对象,在上面挂载很多方法
  • koa封装了ctx属性,并且在上面挂载了path、url等属性,而express没有ctx,所以其属性直接挂载在res和req上
  • koa将router逻辑从express中抽离出来形成koa-router插件,所以express中router和中间件共用一个队列,中间件默认的路由为'/'
  • koa将static从express中抽离出形成koa-static,express自带static
  • koa将views从express中抽离出形成koa-views,express自带static
  • koa将bodyparse从express中抽离出形成koa-bodyparse,express通过body-parser

express的整体框架

从上面的可以知道express的大致框架:

  • express是一个对象,上面挂载了static、view、bodyparse等逻辑方法;express也是一个函数,执行会返回一个app对象
  • app是一个监听请求时的处理函数,也是一个对象上面挂载了很多方法。
const  http = require('http');const url = require('url');cosnt methods = require('methods'); //[get,post.......]function createApplication() {  //监听函数  function app(req, res) {    let method = req.method.toLowerCase();    let { pathname, query } = url.parse(req.url,true);    let index = 0;        // 创建了一个next函数,用来派发中间件、路由,事项洋葱模型    function next() {       if(app.routes.length == index) return res.end(`Cannot found`)      let layer = app.routes[index++];      //这里中间件的处理逻辑和路由的处理逻辑不一样,下面在介绍      layer.handler(req,res,next);    }    next();  }    //内部封装了http模块  app.listen = function () {    let server = http.createServer(app);    server.listen(...arguments)  }    app.routes = [];  //中间件和路由队列    // 批量创建方法:app.get、app.set.....注册路由  methods.forEach(method => {    app[method] = function (pathname, handler) {      let layer = {        pathname,  //路由        handler, //处理函数        method  //路由为get、post....,中间件为middleware      }            //处理带参数的路由/user/:id/:name      let keys = [];      if(pathname.includes(":")){        let regStr = pathname.replace(/:([^/]*)/g,function () {          keys.push(arguments[1]);          return '([^\/]*)'        });        layer.params = keys;    // 路径参数key数组[id,name]        // 转化成正则类似/\/user\/([^\/]*)\/([^\/]*)/来匹配请求路径        // 后面会介绍使用的地方        layer.reg = new RegExp(regStr);       }      app.routes.push(layer);    }  });    app.all = function (pathname, handler) {    let layer = {      pathname,      handler,      method:'all'    }    app.routes.push(layer);  }    // 注册中间件  app.use = function (pathname,handler) {    if(typeof handler === 'undefined'){      handler = pathname;      pathname = '/';  //中间件也是一种路由,所有的路由都能匹配    }    let layer = {      pathname,      handler,      method:'middleware'   //用来和router的方法区别    }    app.routes.push(layer);  }    return app;}module.exports = createApplication;复制代码

扩展res和req

function createApplication() {  function app(req, res) {    let method = req.method.toLowerCase();    let { pathname, query } = url.parse(req.url,true);    let index = 0;    function next() {       if(app.routes.length == index) return res.end(`Cannot found`)      let layer = app.routes[index++];      layer.handler(req,res,next);    }    next();  }  app.listen = function () {    let server = http.createServer(app);    server.listen(...arguments)  }  app.routes = [];     app.use = function (pathname,handler) {    if(typeof handler === 'undefined'){      handler = pathname;      pathname = '/';      }    let layer = {      pathname,      handler,      method:'middleware'    }    app.routes.push(layer);  }    // 用app.use注册了内置中间件来扩展req和res的  app.use(function (req,res,next) {     let method = req.method.toLowerCase();    let { pathname, query } = url.parse(req.url, true);    req.path = pathname;    req.query = query;    req.hostname = req.headers.host.split(':')[0];        //res.send对返回各种类型兼容处理    res.send = function (params) {      res.setHeader('Content-Type', 'text/html;charset=utf8');      if (typeof params === 'object') {//返回json对象        res.setHeader('Content-Type', 'application/json;charset=utf8');        res.end(util.inspect(params));      } else if (typeof (params) === 'number') {//数字对应状态码        res.statusCode = params;        res.end(require('_http_server').STATUS_CODES[params]);      } else {        res.end(params);      }    }        //res.sendFile返回文件    res.sendFile = function (pathname) {      res.setHeader('Content-Type', require('mime').getType(pathname) + ';charset=utf8');      fs.createReadStream(pathname).pipe(res);    }        //res.redirect重定向    res.redirect = function (pathname) {      res.statusCode = 302;      res.setHeader('Location',pathname);      res.end();    }    next();  })  return app;}module.exports = createApplication;复制代码

区分处理中间件和路由

function app(req, res) {    let method = req.method.toLowerCase();    let { pathname, query } = url.parse(req.url,true);    let index = 0;    function next(err) {        let layer = app.routes[index++];        if(layer){            //中间件的处理,包含存在请求路径处理exp:/user/info匹配/user/            if (layer.method === 'middle') {            if (layer.pathname === '/' || req.path.startsWith(layer.pathname + '/') || req.path === layer.pathname) {              return layer.handler(req, res, next); //把控制权next给了用户            } else {              next(); // 匹配不到路径就执行next()匹配下一个中间件            }          } else {            //router处理含请求参数的路由            if (layer.params) {              if (layer.method === method && (layer.reg.test(req.path))) {                                // layer.reg => /\/user\/([^\/]*)\/([^\/]*)/                // req.path => '/user/1/kbz'                // matchers => ['/user/1/kbz','1','2']                // layer.params => [id,name]                // req.params => {id:'1',name:'kbz'}                                let matchers = req.path.match(layer.reg);                req.params = layer.params.reduce((memo, current, index) => (memo[current] = matchers[index + 1], memo), {});                return layer.handler(req, res);              }            }            //router处理不含请求参数的路由            if ((layer.pathname === req.path || layer.pathname === '*') && (layer.method === method || layer.method === 'all')) {              return layer.handler(req, res);            }            next()  //router的处理会自动调用next()          }      }else{        res.end(`Cannot ${pathname} ${method}`);      }    }    next();  }复制代码

next(err)错误处理

function app(req, res) {    let method = req.method.toLowerCase();    let { pathname, query } = url.parse(req.url,true);    let index = 0;    function next(err) {      let layer = app.routes[index++];      if(layer){        if(err){          //处理错误,应该找到错误处理中间件,特点是拥有4个参数          //由用户定义,放在对列最后          if (layer.method === 'middle' && layer.handler.length===4 ){            return layer.handler(err,req,res,next)          }else{            next(err);  //不是错误处理中间件,就向后继续查找          }        }else{          if (layer.method === 'middle') {             if (layer.pathname === '/' || req.path.startsWith(layer.pathname + '/') || req.path === layer.pathname) {              return layer.handler(req, res, next);             } else {              next();             }          } else {            if (layer.params) {              if (layer.method === method && (layer.reg.test(req.path))) {                let matchers = req.path.match(layer.reg);                req.params = layer.params.reduce((memo, current, index) => (memo[current] = matchers[index + 1], memo), {});                return layer.handler(req, res);              }            }            if ((layer.pathname === req.path || layer.pathname === '*') && (layer.method === method || layer.method === 'all')) {              return layer.handler(req, res);            }            next()          }        }      }else{        res.end(`Cannot ${pathname} ${method}`);      }    }    next();  }复制代码

内置view渲染逻辑

使用express渲染逻辑主要是调用res.render方法,其中使用最多的就是ejs模板引擎,ejs渲染逻辑可以参考

app.set('views','view');    //渲染文件目录app.set('view engine','html');  //更改省略的后缀为html,而不是.ejsapp.engine('html',require(ejs').__express); //用ejs模板渲染 复制代码
function createApplication() {    app.use = function (pathname,handler) {        if(typeof handler !== 'function'){        handler = pathname;        pathname = '/';    }    let layer = {      method:'middle',      pathname,      handler    }    app.routes.push(layer);  }    // 配置  app.settings = {}  app.set = function (key,value) {    app.settings[key] = value;  }  app.engines = {}  app.engine = function (ext,renderFn) {    app.engines[ext] = renderFn  }  app.use(function (req,res,next    let method = req.method.toLowerCase();    let { pathname, query } = url.parse(req.url, true);    req.path = pathname;    req.query = query;    req.hostname = req.headers.host.split(':')[0];    res.send = function (params) {      res.setHeader('Content-Type', 'text/html;charset=utf8');      if (typeof params === 'object') {        res.setHeader('Content-Type', 'application/json;charset=utf8');        res.end(util.inspect(params));      } else if (typeof (params) === 'number') {        res.statusCode = params;        res.end(require('_http_server').STATUS_CODES[params]);      } else {        res.end(params);      }    }    res.sendFile = function (pathname) {      res.setHeader('Content-Type', require('mime').getType(pathname) + ';charset=utf8');      fs.createReadStream(pathname).pipe(res);    }    res.redirect = function (pathname) {      res.statusCode = 302;      res.setHeader('Location',pathname);      res.end();    }        //通过render对页面进行渲染    res.render = function (filename,obj) {      let dirname = app.settings['views'] ;      let extname = app.settings['view engine'] ;      let p = path.resolve(dirname,filename+'.'+extname);      app.engines[extname](p,obj,function (data) {        res.end(data);      });    }    next();  })}复制代码

内置static逻辑

这里简化了逻辑:

createApplication.static = function (dir) {  return function (req,res,next) {    let p = req.path === '/' ? '/index.html' : req.path    let realPath = path.join(dir, p);    let flag = fs.existsSync(realPath);    if(flag){      fs.createReadStream(realPath).pipe(res);    }else{      next();    }  }}复制代码

express的bodyparser

express的bodyparser也是通过插件引入

// body-parser.jsfunction urlencoded() {  return (req,res,next)=>{    if (req.headers['content-type']==='application/x-www-form-urlencoded'){      let arr = [];      req.on('data',function (data) {        arr.push(data);      })      req.on('end', function (data) {        req.body = require('querystring').parse(Buffer.concat(arr).toString());        next();      })    }else{      next();    }  }}function json() {  return (req, res, next) => {    if (req.headers['content-type'] === 'application/json') {      let arr = [];      req.on('data', function (data) {        arr.push(data);      })      req.on('end', function (data) {        req.body = JSON.parse(Buffer.concat(arr).toString());        next();      })    } else {      next();    }  }}module.exports.urlencoded = urlencodedmodule.exports.json = json复制代码

结语

前面已经详细介绍了koa的原理和中间件,这里主要是表示express和koa不同的地方,主要的插件逻辑可能都简化了,再次说明express的不同:

  • koa使用了类的概念,express没有使用类,而是直接使用函数对象,在上面挂载很多方法
  • koa封装了ctx属性,并且在上面挂载了path、url等属性,而express没有ctx,所以一些属性和方法直接挂载在res和req上
  • koa将router逻辑从express中抽离出来形成koa-router插件,所以express中router和中间件共用一个队列,中间件默认的路由为'/'
  • koa将static从express中抽离出形成koa-static,express自带static
  • koa将views从express中抽离出形成koa-views,express自带res.render
  • koa将bodyparse从express中抽离出形成koa-bodyparse,express自带bodyparse

作者:梦想攻城狮
链接:https://juejin.im/post/5bb0356ae51d450e8108ca8f
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的文章
Flutter Boost的router管理
查看>>
Android Flutter混合编译
查看>>
微信小程序 Audio API
查看>>
[React Native]react-native-scrollable-tab-view(进阶篇)
查看>>
Vue全家桶+Mint-Ui打造高仿QQMusic,搭配详细说明
查看>>
React Native for Android 发布独立的安装包
查看>>
React Native应用部署/热更新-CodePush最新集成总结(新)
查看>>
react-native-wechat
查看>>
基于云信的react-native聊天系统
查看>>
网易云音乐移动客户端Vue.js
查看>>
ES7 await/async
查看>>
ES7的Async/Await
查看>>
React Native WebView组件实现的BarCode(条形码)、(QRCode)二维码
查看>>
每个人都能做的网易云音乐[vue全家桶]
查看>>
JavaScript专题之数组去重
查看>>
Immutable.js 以及在 react+redux 项目中的实践
查看>>
Vue2.0全家桶仿腾讯课堂(移动端)
查看>>
React+Redux系列教程
查看>>
react-native 自定义倒计时按钮
查看>>
19 个 JavaScript 常用的简写技术
查看>>