内容简介:kao和express都是同一个团队开发的,从上面的可以知道express的大致框架:使用express渲染逻辑主要是调用res.render方法,其中使用最多的就是ejs模板引擎,ejs渲染逻辑可以参考
kao和express都是同一个团队开发的, koa框架会用也会写—(koa的实现) 已经介绍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渲染逻辑可以参考 koa框架会用也会写—(koa-view、koa-static)
app.set('views','view'); //渲染文件目录 app.set('view engine','html'); //更改省略的后缀为html,而不是.ejs app.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.js function 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 = urlencoded module.exports.json = json 复制代码
结语
前面 koa框架会用也会写—(koa的实现) 已经详细介绍了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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- php如何实现session,自己实现session,laravel如何实现session
- AOP如何实现及实现原理
- webpack 实现 HMR 及其实现原理
- Docker实现原理之 - OverlayFS实现原理
- 为什么实现 .NET 的 ICollection 集合时需要实现 SyncRoot 属性?如何正确实现这个属性?
- 自己实现集合框架(十):顺序栈的实现
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Web全栈工程师的自我修养
余果 / 人民邮电出版社 / 2015-9-1 / 49.00
全栈工程师正成为 IT 行业的新秀,论是上市互联网公司还是创业公司,都对全栈工程师青睐有加。本书作者是腾讯公司高级工程师,在前端、后端和APP开发方面都有丰富的经验,在本书中分享了全栈工程师的技能要求、核心竞争力、未来发展方向、对移动端的思考。除此之外,本书还详细记录了作者从零开始、学习成长的心路历程。 本书内容全面,客观务实,适合互联网行业新人、程序员,以及期待技术转型的从业者阅读参考。一起来看看 《Web全栈工程师的自我修养》 这本书的介绍吧!