介绍
Node.js是在各种环境中执行JavaScript的工具。JavaScript最初只是一种仅存在于Web浏览器中的语言,但起初却很不起眼,但是Node.js项目扩大了它的范围,并使其成为世界上最受欢迎的编程语言。Node.js具有Web开发经验,可以扩展人们的创造力,使新一代开发人员可以创建服务器,命令行工具,桌面应用程序甚至机器人。
十二年之前的Ryan Dahl首次创建,现在是上最大的开源项目。这和NodeJS本身的特点息息相关。专为构建可扩展的Web服务端的应用而设计。自身具有高并发性和可扩展性。
本文我想了解一下express/koa/hapi三个NodeJS工程框架的区别。
Express是一个最小且灵活的Web应用程序框架,为Web应用和移动应用程序提供一组强大的功能。它的行为更像一个中间件,帮助我们管理服务和路由。
1. 安装 npm /yarn
// 你可以将express安装到项目依赖
npm install express --save
// 如果要临时安装Express而不是将其添加到依赖项列表,则可以使用
npm install express --no-save
2. 使用
// 基本使用
// app.js
const express = require('express') // 引入
const app = express() // 实例化express对象
const port = 3000 || env.process.port; // 端口默认
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
// 使用指令Node app.js 启动服务
// 然后,在浏览器中输入 http://localhost:3000/ 以查看输出。
express-generator生成器
// 可使用应用程序生成器工具 (express-generator) 快速创建应用程序框架。
1. 安装 npm install -g express-generator/npx express-generator
2. 启动生成 express
// express --view=pug myapp 指定HTML模版和名称 ejs|hbs|hjs|jade|pug|twig|vash // --git //add .gitignore
3. 安装依赖性 npm i
4. 运行环境 DEBUG=myapp:* npm start => mac/linux. // set DEBUG=myapp:* & npm start => window
路由
// 基本路由 路由结构
app.METHOD(PATH, HANDLER)
/*
app 是 express 的实例。
METHOD 是 HTTP 请求方法。
PATH 是服务器上的路径。
HANDLER 是在路由匹配时执行的函数。
*/
// 路由方法
// 路由方法派生自 HTTP 方法之一,附加到 express 类的实例。
// 有一种特殊路由方法:app.all(),它并非派生自 HTTP 方法。该方法用于在所有请求方法的路径中装入中间件函数。
//您可以提供多个回调函数,以类似于中间件的行为方式来处理请求。唯一例外是这些回调函数可能调用 next('route') 来绕过剩余的路由回调。您可以使用此机制对路由施加先决条件,在没有理由继续执行当前路由的情况下,可将控制权传递给后续路由。
app.get('/example/b', function (req, res, next) {
console.log('the response will be sent by the next function ...');
next();
}, function (req, res) {
res.send('Hello from B!');
});
//独立函数与一组函数的组合可以处理一个路由
app.route
//使用 app.route() 为路由路径创建可链接的路由处理程序。
app.route('/book')
.get(function(req, res) {
res.send('Get a random book');
})
.post(function(req, res) {
res.send('Add a book');
})
.put(function(req, res) {
res.send('Update the book');
});
static.router 中间件
使用 express.Router 类来创建可安装的模块化路由处理程序。Router 实例是完整的中间件和路由系统;因此,常常将其称为“微型应用程序”。
// router.js
const express = require('express'); //引入
let router = express.Router(); // 实例化router
// middleware that is specific to this router,用于显示日期
router.use(function timeLog(req, res, next) {
console.log('Time: ', Date.now());
next();
});
// 定义home路由
router.get('/', function(req, res) {
res.send('Birds home page');
});
// 定义about路由
router.get('/about', function(req, res) {
res.send('About birds');
});
module.exports = router; //导出router实例
// app.js
// 在应用程序中使用router模块
let router = require('./router'); // 引入
...
app.use('/birds', router); //
静态文件的处理
// 为了提供诸如图像、CSS 文件和 JavaScript 文件之类的静态文件。
app.use(express.static('public')); //名为 public 的目录中提供图像、CSS 文件和 JS 文件。
//要使用多个静态资源目录,请多次调用 express.static 中间件函数
//要为 express.static 函数提供的文件创建虚拟路径前缀
app.use('/static', express.static('public'));
//然而,向 express.static 函数提供的路径相对于您在其中启动 node 进程的目录。如果从另一个目录运行 Express 应用程序,那么对于提供资源的目录使用绝对路径会更安全
app.use('/static', express.static(__dirname + '/public'));
注: “__dirname” 是 node.js 中的一个全局变量,它指向当前脚本所在的目录。 “__dirname” 变量值代表程序运行的根目录。
响应方式
方法 | 描述 |
---|---|
res.download() | 提示将要下载文件 |
res.end() | 结束响应进程。 |
res.json() | 发送 JSON 响应。 |
res.jsonp() | 在 JSONP 的支持下发送 JSON 响应。 |
res.redirect() | 重定向请求。 |
res.render() | 呈现视图模板。 |
res.send() | 发送各种类型的响应。 |
res.sendFile() | 以八位元流形式发送文件。 |
res.sendStatus() | 设置响应状态码并以响应主体形式发送其字符串表示。 |
中间件
中间件是express的重点,介绍几个常用的
- body-parser 中间件
用于接收并处理post请求的数据
// body-parser
// npm 安装
npm install body-parser
// 接受post的请求的参数并处理
2. 引入
3. 配置
- cookie-parser 中间件
用于存储临时信息,当用户再次登录可以实现记住密码,也可以实现在未登录情况下实现添加购物车,同时也可以实现商品信息的浏览记录。当然需要加密了。
// cookie-parser
//1. npm 安装
npm install cookie-parser
// 2. 引入
var cookieParser = require('cookie-parser')
// 3. 配置
app.use(cookieParser())
// 4. 使用
res.cookie(..)
// 实现页面的数据共享
// 设置cookie(maxAge过期时间,path设置哪些页面可以访问cookie, domain设置多个域名共享cookie,httpOnly, secure,signed:cookie的加密)
res.cookie("username", "conan", {maxAge:1000*60*60})
// cookie的加密
// 1. 先传入加密的密钥 app.use(cookieParser('xxxxxx'))
// 2. 设置启动加密设置 res.cookie(... {...signed:true})
// 3. 获取加密cookie的方式:res.signedCookies
//express-session的使用
// 1. npm 安装
npm install express-session
// 2. 引入
var session = require('express-session')
// 3. 配置
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
cookie: { secure: true }
}))
// 销毁方式
1.res.session.cookies.maxAge = 0; //缺点:会销毁所有的cookie
2. 指定销毁
3. req.session.destory() // api销毁
// 负载均衡
// 将session的数据存储在数据库中 mongodb/mysql
// 1. npm 安装
npm install connect-mongo
// 2. 引入
const session = require('express-session');
const MongoStore = require('connect-mongo');
// 3. 配置
app.use(session({
store: MongoStore.create({ // 将生成的sessions信息存储到数据库中
mongoUrl: 'mongodb://127.0.0.1:27017/session',
touchAfter: 24*3600, // 24h内只更新一次sessions
})
}));
// 将session的数据存储到redis中
// 1.npm 安装
npm install redis connect-redis express-session
总结
好处
- 几乎是Node.js Web中间件的标准
- 简单,简约,灵活和可扩展
- 快速开发应用程序
- 完全可定制
- 学习曲线低
- 轻松集成第三方服务和中间件
- 主要关注浏览器,模板和渲染集成开箱即用
缺点
- 组织需要非常清楚,以避免在维护代码时出现问题
- 随着代码库大小的增加,重构变得非常具有挑战性
- 需要大量的手工劳动,因为您需要创建所有端点
- 使用gzip压缩
- 不要使用同步功能
- 正确记录(用于调试,使用特殊模块,如调试,应用程序活动使用winston或bunyan)
- 使用try-catch或promises正确处理异常
- 确保您的应用程序使用流程管理器自动重新启动,或使用systemd或upstartinit等系统
- 在群集中运行您的应用。您可以通过启动进程集群来大大提高Node.js应用程序的性能
- 缓存请求结果,以便您的应用不会重复操作以反复提供相同的请求
- 使用负载均衡器运行它的多个实例并分配流量,如Nginx或HAProxy
- 对静态资源使用反向代理。它可以处理错误页面,压缩,缓存,提供文件和负载平衡等
最佳安全实践
Node.js漏洞直接影响Express,因此确保使用最新的稳定版Node.js
Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async 函数,Koa 帮你丢弃回调函数,并有力地增强错误处理。 Koa 并没有捆绑任何中间件, 而是提供了一套优雅的方法,帮助您快速而愉快地编写服务端应用程序。
1.安装
$ npm i koa. //安装
$ node my-koa-app.js// 启动
2. 使用
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
ctx.body = 'Hello World';
});
app.listen(3000);
上下文context
Koa Context 将node的request和response对象封装到单个对象中。
每个请求创建一个Context.使用标识符ctx在中间件中作为接收器引用。
app.use(async ctx => {
ctx;//这是一个context
ctx.request; // Koa request
ctx.response; // Koa response
})
为了方便起见,将许多上下文的访问器和方法直接委托给ctx.responst和ctx.response。
比如:ctx.type/ctx.length 委托给respons对象; ctx.path/ctx.method委托给request
中间件
// koa的中间件的执行顺序。洋葱模式
ctx.state = {
// 公共的内容
}
//处理Post请求数据
//方式一
//方式二: npm i koa-bodyparser --save
总结
好处
- Koa提高了互操作性,健壮性,使编写中间件变得更加愉快。
- 集成了大量的web API,但是没有绑定中间件
- 非常轻量,核心的Koa模块只有大约2K行代码
- 拥有非常好的用户体验
- 通过try / catch更好地处理错误
- 异步控制流,代码可读性更高
缺点
- Koa社区相对较小
- 与Express风格的中间件不兼容(目前还有遇到与其他框架兼容的中间件)
性能
- Koa本身是一个非常轻量级的框架,可以构建具有出色性能的Web应用程序。代码可读性和维护性都相对较高
最佳性能实践
- 集群
- 并行运行
- 在代码中使用异步API
- 保持代码小而轻
- 以及使用gzip压缩 等等
最佳安全实践
- Koa有大量的中间件,提供相应的功能
Hapi是基础功能相对丰富的框架。开发人员更专注于业务,而不是花时间构建基础架构。配置驱动的模式,区别于传统的web服务器操作。他还有比一个独特功能,能够在特定的IP上创建服务器,具有类似的功能onPreHandler。再需要的时候你可以拦截特地的请求做一些必要的操作
1. 安装
npm install hapi
2. 使用
'use strict';
const Hapi=require('hapi');
// 创建一个服务监听8000端口
const server=Hapi.server({
host:'localhost',
port:8000
});
// 添加路由
server.route({
method:'GET',
path:'/hello',
handler:function(request,h) {
return'hello world';
}
});
// 启动服务
const start = async function() {
try {
await server.start();
}
catch (err) {
console.log(err);
process.exit(1);
}
console.log('Server running at:', server.info.uri);
};
start();
总结
好处
- 提供了一个强大的插件系统,允许您快速添加新功能和修复错误
- 可扩展的API
- 对请求处理有更深层次的控制。
- 创建(REST)api的最佳选择,提供了路由、输入、输出验证和缓存
- 一次编写适配各端
- 详细的API参考和对文档生成的良好支持
- 与任何前端框架(如React,Angular和Vue.js)一起使用来创建单页面应用程序
- 基于配置的伪中间件
- 提供缓存,身份验证和输入验证
- 提供基于插件的扩展架构
- 提供非常好的企业插件,如joi,yar,catbox,boom,tv和travelogue
缺点
- 代码结构复杂
- 插件不兼容,只能使用指定的插件如:catbox joi boom tv good travelogue等
- 端点是手动创建的,必须手动测试
- 重构是手动的
性能
017年对Node框架的研究表明hapi相对于其他框架的表现最差
最后Express仍然是当下最为流行,koa因拥抱ES6正在崛起,hapi还是大型项目的第一选择
// web应用框架的发展
// express
express 为第一代的web应用框架,对http进行了封装,用起来十分简单。但是expres是基于ES5进行开发的,所以实现异步程序的方法就是使用-回调函数。确实难受。。。。。
const express = require('express')
const app = new express();
app.get('/', (req,res) => {
res.send('hello world');
});
app.get('/test', (req,res) => {
fs.readFile('/file1', (err,data) => {
if(err) res.stauts(500).send('read file1 error');
fs.readFile('/file2', (err,data) => {
if(err) res.status(500).send('read file2 error');
res.type('text/plain');
res.send(data);
})
})
})
app.listen(3000, () => {
console.log("starting the 3000 port now");
})
// koa1.0
随着NodeJS支持ES6的语法之后,Exprss 开发团队也开始开发新的开发工具来适应ES6的新语法。
与Express相比,koa1.0使用的是genertor实现的异步。
const koa = require('koa');
var app = new koa();
app.get('/test', function *() {
yield doReadFile1();
var data = yield doReadFile2();
this.body = data;
})
app.listen(3000)
// genertor的本意并不是异步,而promise才是,但是promis实现起来不太容易。所以采用ES7中的async/await实现。
async function() {
var data = await fs.read('/file1')
}
// koa2.x
完全使用ES6中async/await实现了异步操作
app.use(async (ctx, next) => {
await next();
var data = await doReadFile();
ctx.response.type = 'text/plain';
ctx.response.body = data;
})
// 更加简明的写法
app.use(async ctx => {
ctx.body = 'hello';
})