NodeJS框架Express与Koa对比

框架介绍

express框架是一个基于 Node.js 平台的极简、灵活的 web 应用开发框架,主要基于 Connect 中间件,并且自身封装了路由、视图处理等功能。

koa是 Express 原班人马基于 ES6 新特性重新开发的框架,主要基于 co 中间件,框架自身不包含任何中间件,很多功能需要借助第三方中间件解决,但是由于其基于 ES6 generator 特性的异步流程控制,解决了 “callback hell” 和麻烦的错误处理问题。

相同点
两个框架都对http进行了封装。相关的api都差不多,同一批人所写。

不同点
express内置了许多中间件可供使用,而koa没有。

express包含路由,视图渲染等特性,而koa只有http模块。

express的中间件模型为线型,而koa的中间件模型为U型,也可称为洋葱模型构造中间件。

express通过回调实现异步函数,在多个回调、多个中间件中写起来容易逻辑混乱。

一、起步:创建一个简单的服务器

原生node

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var http = require('http');

http.createServer(function (request, response) {

// 发送 HTTP 头部
// HTTP 状态值: 200 : OK
// 内容类型: text/plain
response.writeHead(200, {'Content-Type': 'text/plain'});

// 发送响应数据 "Hello World"
response.end('Hello World\n');
}).listen(8888);

// 终端打印如下信息
console.log('Server running at http://127.0.0.1:8888/');
//或
const app=http.createServer()
app.on('request',(req,res)=>{})

express

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//express_demo.js 文件
var express = require('express');
var app = express();

app.get('/', function (req, res) {
res.send('Hello World');
})

var server = app.listen(8081, function () {

var host = server.address().address
var port = server.address().port

console.log("应用实例,访问地址为 http://%s:%s", host, port)

})

koa

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 导入koa,和koa 1.x不同,在koa2中,我们导入的是一个class,因此用大写的Koa表示:
const Koa = require('koa');

// 创建一个Koa对象表示web app本身:
const app = new Koa();

// 对于任何请求,app将调用该异步函数处理请求:
app.use(async (ctx, next) => {
await next();
ctx.response.type = 'text/html';
ctx.response.body = '<h1>Hello, koa2!</h1>';
});

// 在端口3000监听:
app.listen(3000);
console.log('app started at port 3000...');

二、请求和响应

express

1
2
3
app.get('/', function (req, res) {
// request 和 response 对象来处理请求和响应的数据
})

koa
参数ctx是由koa传入的封装了request和response的变量,我们可以通过它访问request和response,next是koa传入的将要处理的下一个异步函数。

1
2
3
4
5
6
7
async (ctx, next) => {
await next();
// 设置response的Content-Type:
ctx.response.type = 'text/html';
// 设置response的内容:
ctx.response.body = '<h1>Hello, koa2!</h1>';
}

三、路由

原生node

1
2
3
4
var server = require("./server");
var router = require("./router");

server.use(router.route);

express

1
2
3
4
5
6
7
8
9
10
//  GET 请求
app.get('/', function (req, res) {
console.log("主页 GET 请求");
res.send('Hello GET');
})
// POST 请求
app.post('/', function (req, res) {
console.log("主页 POST 请求");
res.send('Hello POST');
})

koa

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//  GET 请求    写法1:
app.use(async (ctx, next) => {
if (ctx.request.path === '/') {
ctx.response.body = 'index page';
} else {
await next();
}
});
// GET 请求 写法2:
const Koa = require('koa');
const router = require('koa-router')();// 注意require('koa-router')返回的是函数:
const app = new Koa();

// add url-route:
router.get('/hello/:name', async (ctx, next) => {
var name = ctx.params.name;
ctx.response.body = `<h1>Hello, ${name}!</h1>`;
});

router.get('/', async (ctx, next) => {
ctx.response.body = '<h1>Index</h1>';
});

// add router middleware:
app.use(router.routes());

app.listen(3000);
console.log('app started at port 3000...');

四、静态文件

express

1
2
//  Express 提供了内置的中间件 express.static 来设置静态文件如:img, CSS, JavaScript 等。
app.use('/public', express.static('public'));

koa

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 方法一:自己写一个处理方法
let staticFiles = require('./static-files');
app.use(staticFiles('/static/', __dirname + '/static'));

// 方法二:引一个包
const Koa = require('koa');
const app = new Koa();
const bodyParser = require('koa-bodyparser');
const Router = require('koa-router');
const router = new Router();
const render = require('koa-ejs');
const path = require('path');
const serve = require('koa-static');
app.use(bodyParser());

/**静态资源(服务端) */
app.use(serve(path.join(__dirname + "/public")));

// 初始化ejs,设置后缀为html,文件目录为`views`
render(app, {
root: path.join(__dirname, 'views'),
layout: false,
viewExt: 'html',
cache: false,
debug: false
});
// 监听3000端口
app.listen(3000);

五、get\post传参接收方法

原生node

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// get 
var http = require('http');
var url = require('url');
var util = require('util');

http.createServer(function(req, res){
res.writeHead(200, {'Content-Type': 'text/plain'});

// 解析 url 参数
var params = url.parse(req.url, true).query;
res.write("网站名:" + params.name);
res.write("\n");
res.write("网站 URL:" + params.url);
res.end();

}).listen(3000);
// post
var http = require('http');
var querystring = require('querystring');

http.createServer(function (req, res) {
var body = "";
req.on('data', function (chunk) {
body += chunk;
});
req.on('end', function () {
// 解析参数
body = querystring.parse(body);
// 设置响应头部信息及编码
res.writeHead(200, {'Content-Type': 'text/html; charset=utf8'});

if(body.name && body.url) { // 输出提交的数据
res.write("网站名:" + body.name);
res.write("<br>");
res.write("网站 URL:" + body.url);
} else { // 输出表单
res.write(postHTML);
}
res.end();
});
}).listen(3000);

express
get 传参接收方法

1
2
3
4
5
6
7
8
9
10
app.get('/process_get', function (req, res) {

// 输出 JSON 格式
var response = {
"first_name":req.query.first_name,
"last_name":req.query.last_name
};
console.log(response);
res.end(JSON.stringify(response));
})

post传参接收方法

1
2
3
4
5
6
7
8
9
10
app.post('/process_post', urlencodedParser, function (req, res) {

// 输出 JSON 格式
var response = {
"first_name":req.body.first_name,
"last_name":req.body.last_name
};
console.log(response);
res.end(JSON.stringify(response));
})

koa

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// POST
const bodyParser = require('koa-bodyparser');
router.post('/signin', async (ctx, next) => {
var
name = ctx.request.body.name || '',
password = ctx.request.body.password || '';
console.log(`signin with name: ${name}, password: ${password}`);
if (name === 'koa' && password === '12345') {
ctx.response.body = `<h1>Welcome, ${name}!</h1>`;
} else {
ctx.response.body = `<h1>Login failed!</h1>
<p><a href="/">Try again</a></p>`;
}
});
app.use(bodyParser());

六、cookie管理

express
使用中间件向 Node.js 服务器发送 cookie 信息

1
2
3
4
5
6
7
8
9
10
11
12
13
// express_cookie.js 文件
var express = require('express')
var cookieParser = require('cookie-parser')
var util = require('util');

var app = express()
app.use(cookieParser())

app.get('/', function(req, res) {
console.log("Cookies: " + util.inspect(req.cookies));
})

app.listen(8081)