当前位置: 首页 > 工具软件 > rest-hapi > 使用案例 >

NodeJS-框架express-Koa-Hapi的区别

卫梓
2023-12-01

介绍
Node.js是在各种环境中执行JavaScript的工具。JavaScript最初只是一种仅存在于Web浏览器中的语言,但起初却很不起眼,但是Node.js项目扩大了它的范围,并使其成为世界上最受欢迎的编程语言。Node.js具有Web开发经验,可以扩展人们的创造力,使新一代开发人员可以创建服务器,命令行工具,桌面应用程序甚至机器人。
十二年之前的Ryan Dahl首次创建,现在是上最大的开源项目。这和NodeJS本身的特点息息相关。专为构建可扩展的Web服务端的应用而设计。自身具有高并发性和可扩展性。
本文我想了解一下express/koa/hapi三个NodeJS工程框架的区别。

Express

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 
// 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

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

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还是大型项目的第一选择

比较Express 和 Koa/Koa2

// 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';
})
 类似资料: