如何理解express框架
nodejs web 服务器
const http = require('http')
const hostname = '127.0.0.1'
const port = 3000
function callback(req, res) {
res.statusCode = 200
res.setHeader('Content-Type', 'text/plain')
res.end('Hello World\n')
}
const server = http.createServer(callback)
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`)
})
http
的 createServer()
方法创建新的 HTTP 服务器并返回。每当接收到新请求时,都会调用 request
事件,即执行callback 回调,其提供两个对象:请求(http.IncomingMessage
对象)和响应(http.ServerResponse
对象)。
Node.js 是一个底层平台,为了让开发者的工作变得轻松有趣,社区在 Node.js 上构建了数千个库,比如: express。
express
提供了最简单而强大的方式来创建 Web 服务器,它的极简主义方法、没有偏见、专注于服务器的核心功能,是其成功的关键。
express 的使用
import express from 'express'
const app = express()
app.get('/', (req, res, next) => {
res.end('hello world')
})
app.listen('7001', () => {
console.log('listen at 7001')
})
下面我们从源码的角度简要的梳理下express的执行过程。
express 源码简要解析
var bodyParser = require('body-parser')
var EventEmitter = require('events').EventEmitter;
var mixin = require('merge-descriptors');
var proto = require('./application');
var Route = require('./router/route');
var Router = require('./router');
var req = require('./request');
var res = require('./response');
/**
* Expose `createApplication()`.
*/
// 暴露出一个createApplication方法
exports = module.exports = createApplication;
/**
* Create an express application. 创建一个express的应用
*/
// 返回一个app,这个app是一个函数,同时在函数下面挂载了很多属性
function createApplication() {
var app = function(req, res, next) {
app.handle(req, res, next);
};
// 往app上挂载属性
mixin(app, EventEmitter.prototype, false);
mixin(app, proto, false);
// 以req为原型生成request对象,同时在request挂载app
app.request = Object.create(req, {
app: { configurable: true, enumerable: true, writable: true, value: app }
})
// 以res为原型生成response对象,同时在response挂载app
app.response = Object.create(res, {
app: { configurable: true, enumerable: true, writable: true, value: app }
})
app.init();
// 返回app
return app;
}
/**
* Expose the prototypes.
*/
// 因为createApplication = exports,所以下面的代码就是在createApplication上挂载一些属性
exports.application = proto;
exports.request = req;
exports.response = res;
/**
* Expose constructors. 往createApplication挂载 路由的构造函数 Route 和 Router
*/
exports.Route = Route;
exports.Router = Router;
/**
* Expose middleware 在createApplication挂载一些属性
*/
exports.json = bodyParser.json
exports.query = require('./middleware/query');
exports.raw = bodyParser.raw
exports.static = require('serve-static');
exports.text = bodyParser.text
exports.urlencoded = bodyParser.urlencoded
/**
* Replace removed middleware with an appropriate error message.
*/
// 下面的这些中间件不在绑定到express上,如果需要则需单独安装,express保持基本的功能即可
var removedMiddlewares = [
'bodyParser',
'compress',
'cookieSession',
'session',
'logger',
'cookieParser',
'favicon',
'responseTime',
'errorHandler',
'timeout',
'methodOverride',
'vhost',
'csrf',
'directory',
'limit',
'multipart',
'staticCache'
]
removedMiddlewares.forEach(function (name) {
Object.defineProperty(exports, name, {
get: function () {
throw new Error('Most middleware (like ' + name + ') is no longer bundled with Express and must be installed separately. Please see https://github.com/senchalabs/connect#middleware.');
},
configurable: true
});
});
下面主要分析核心代码:
function createApplication() {
var app = function(req, res, next) {
app.handle(req, res, next);
};
// 往app上挂载属性
mixin(app, EventEmitter.prototype, false);
mixin(app, proto, false);
// 以req为原型生成request对象,同时在request挂载app
app.request = Object.create(req, {
app: { configurable: true, enumerable: true, writable: true, value: app }
})
// 以res为原型生成response对象,同时在response挂载app
app.response = Object.create(res, {
app: { configurable: true, enumerable: true, writable: true, value: app }
})
app.init();
// 返回app
return app;
}
var app = express()
var app = express()
执行后返回一个app
,这个app其实是一个函数:
var app = function(req, res, next) {
app.handle(req, res, next);
};
在这个函数app下挂载了很多属性:
var proto = require('./application');
mixin(app, EventEmitter.prototype, false);
mixin(app, proto, false);
app到底是什么?
application.js
代码如下,就是往app上挂载很多属性:
var app = exports = module.exports = {};
app.use = () => {}
app.route = () => {}
// 在app上把get, post等方法挂载上去
methods.forEach(function(method){
app[method] = function(path){
if (method === 'get' && arguments.length === 1) {
// app.get(setting)
return this.set(path);
}
// 在this上生成一个Router的实例
this.lazyrouter();
var route = this._router.route(path);
route[method].apply(route, slice.call(arguments, 1));
return this;
};
});
app.listen = function listen() {
var server = http.createServer(this);
return server.listen.apply(server, arguments);
};
每当请求过来的时候,就会执行http.createServer
里面的回调函数,即app(app是一个函数)。所以,app到底什么呢?app就是一个回调函数,每当发起请求的时候就会执行app,然后把req, res
传入,然后执行:
var app = function(req, res, next) {
app.handle(req, res, next);
};
app.handle = function handle(req, res, callback) {
var router = this._router;
......
// 在这里就来处理路由的逻辑,并返回给前端
router.handle(req, res, done);
};
在项目中我们写的路由,都会被收集起来,然后根据req的method和path来匹配,匹配后就执行对应的逻辑。
app.get('/', (req, res, next) => {
res.end('hello world')
})
var express = require('express')
var router = express.Router()
router.get('/list', function (req, res, next) {})
express-generator脚手架
生成express项目
npm install express-generator -g
express express-test
npm install && npm start
用脚手架生成的项目是一个全栈的项目,包括服务端和前端页面,目前流行的开发模式是前后端分离,前端开发前端的,后端开发后端的。所以我们后端只要求开发接口,就可以忽略这两个文件。
通过--no-view可以创建不带前端页面的项目:
express express-test --no-view
app.js 详解
// 如果找不到页面,就返回404
var createError = require('http-errors')
var express = require('express')
var path = require('path')
var fs = require('fs')
// 解析cookie
var cookieParser = require('cookie-parser')
// 日志模块
var logger = require('morgan')
// 路由
const blogRouter = require('./routes/blog')
const userRouter = require('./routes/user')
// app就是上面解析的回调函数,但是每次不同的请求,里面的req,res不同
var app = express()
app.use(logger('dev'))
// 下面两行是对post方法传递的数据的处理,处理后的数据放在req.body上
// express.json()只处理json格式的post
app.use(express.json())
// 这个处理除了json格式的其他post数据,比如表单数据,表单数据格式就是x-www-form-urlencoded
app.use(express.urlencoded({ extended: false }))
app.use(cookieParser())
// 返回静态文件,我们这里是api开发,不涉及到前端
// app.use(express.static(path.join(__dirname, 'public')));
// 注册路由 这里的路径和blogRouter里面的路径进行合并,也就是这里的路径是一个父路径
app.use('/api/blog', blogRouter)
app.use('/api/user', userRouter)
// 下面是用来捕获错误的代码
// catch 404 and forward to error handler
app.use(function (req, res, next) {
next(createError(404))
})
// error handler
app.use(function (err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message
res.locals.error = req.app.get('env') === 'dev' ? err : {}
// render the error page
res.status(err.status || 500)
res.render('error')
})
module.exports = app
启动文件 bin/www
// 引用app.js导出的app
var app = require('../app');
var http = require('http');
var server = http.createServer(app);
server.listen(port);
package.json
"scripts": {
"start": "node ./bin/www",
"dev": "cross-env NODE_ENV=dev nodemon ./bin/www",
"prd": "cross-env NODE_ENV=production nodemon ./bin/www"
},
转载自:https://juejin.cn/post/7110894763884150792