本文共 11138 字,大约阅读时间需要 37 分钟。
kao和express都是同一个团队开发的,已经介绍koa的原理,而koa在express的基础上进行了优化:
从上面的可以知道express的大致框架:
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;复制代码
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(); }复制代码
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(); }复制代码
使用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(); })}复制代码
这里简化了逻辑:
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也是通过插件引入
// 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的不同: