写一个服务,从NodeJs到Express到Koa

写一个NodeJs服务

创建node-service文件夹,初始化项目

bash 复制代码
mkdir node-service
cd node-service
npm init -y

创建一个简单的HTTP服务器。以下是一个使用Node.js的核心模块http来创建最基础的Node服务的示例:

javascirpt 复制代码
const http = require('http');

// 创建服务器
const server = http.createServer((req, res) => {
  // 设置响应头
  res.writeHead(200, {'Content-Type': 'text/plain'});

  // 发送响应内容
  res.end('Hello, World!');
});

// 监听端口
const port = 3000;
server.listen(port, () => {
  console.log(`Server running on port ${port}`);
});

写一个Express服务

创建express-service文件夹,初始化项目

bash 复制代码
mkdir express-service
cd express-service
npm init -y
npm install express

创建一个简单的HTTP服务器。以下是一个使用Express来创建最基础的Node服务的示例:

javascirpt 复制代码
const express = require('express');

// 创建Express应用程序
const app = express();

// 定义路由
app.get('/', (req, res) => {
  res.send('Hello, World!');
});

// 启动服务器
const port = 3000;
app.listen(port, () => {
  console.log(`Server running on port ${port}`);
});

相比于原生NodeJs,express增强了什么?

Express是一个基于Node.js的Web应用程序框架,它在原生的Node.js基础上提供了许多增强功能,使得构建Web应用更加简单和高效。下面是Express相对于原生Node.js的一些主要增强点:

1.路由处理:Express提供了简洁而强大的路由处理机制,可以轻松定义和管理各种路由。通过使用Express的路由功能,你可以根据不同的URL路径和HTTP方法来处理请求,使得代码结构更加清晰和可维护。

2.中间件支持:Express引入了中间件的概念,允许你在请求和响应之间插入功能模块。这使得处理请求的过程更加灵活,你可以通过添加中间件来处理身份验证、日志记录、错误处理等功能,而无需在每个路由处理程序中重复代码。

3.模板引擎:Express支持多种模板引擎,如EJS、Handlebars和Pug(以前称为Jade)。这使得在服务器端生成动态HTML页面变得更加方便,你可以使用模板引擎来组织和渲染页面,将数据动态地注入到模板中。

4.错误处理:Express提供了内置的错误处理机制,使得处理和捕获错误变得更加容易。你可以定义错误处理中间件来集中处理应用程序中的错误,从而提高代码的可读性和可维护性。

5.静态文件服务:Express可以轻松地为静态文件(如CSS、JavaScript和图像文件)提供服务。你只需指定静态文件所在的目录,Express将自动处理静态文件的请求,无需手动编写路由。

6.插件生态系统:Express拥有庞大的插件生态系统,你可以使用各种第三方插件来扩展Express的功能。这些插件提供了各种功能,如会话管理、身份验证、数据库集成等,帮助你更快地构建复杂的Web应用。

总的来说,Express提供了一种更加简洁、灵活和高效的方式来构建Web应用。它简化了开发过程,提供了许多有用的功能和工具,使得构建和维护Web应用变得更加容易。

路由处理方面,Express做了什么?

查看Express官方文档关于路由的指南,Express提供了get,post,all方法,匹配路由路径,route方法和router对象,方便我们编写路由相关的代码。我们看看原生Nodejs怎么处理路由:

javascirpt 复制代码
const server = http.createServer((req, res) => {
  // 获取请求的URL和方法
  const url = req.url;
  const method = req.method;

  // 处理不同的路由和请求方法
  if (method === 'GET' && url === '/') {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Hello, World!');
  } else if (method === 'GET' && url === '/about') {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('About Page');
  } else {
    res.writeHead(404, { 'Content-Type': 'text/plain' });
    res.end('Not Found');
  }
});

可以看出想实现跟Express一样的路由功能,使用nodejs会麻烦很多。

额外的,为了更好的处理路由,也就说处理请求-响应。Express增强了res和req对象。Express的req对象具有许多有用的属性和方法,如req.params用于获取路由参数,req.query用于获取查询参数,req.body用于获取请求体等。而Express的res对象也提供了许多常用的方法,如res.send()用于发送响应,res.json()用于发送JSON响应,res.redirect()用于重定向等。我们看看原生Nodejs怎么处理请求参数和给出响应:

javascirpt 复制代码
// Nodejs获取路由参数、查询参数、请求体要写一下代码, 而express中可以直接从req对象中获取。
const parsedUrl = url.parse(req.url, true);
// routeParams数组包含了所有的路由参数  
const routeParams = parsedUrl.pathname.split('/').slice(1);
// queryParams对象包含了所有的查询参数
const queryParams = parsedUrl.query;
let body = '';
req.on('data', (chunk) => {
    body += chunk;
});
req.on('end', () => {
    const requestBody = JSON.parse(body);
    // requestBody对象包含了请求体参数
    console.log(requestBody);
});

// 原生nodejs怎样实现express的res.json()
res.setHeader('Content-Type', 'application/json');  
const jsonData = { message: 'Hello, world!' };
const jsonStr = JSON.stringify(jsonData);
res.end(jsonStr);

// 原生nodejs怎样实现express的res.redirect()
res.writeHead(302, {
    'Location': 'https://example.com'
});
res.end();

中间件方面,Express做了什么?

我们分析一个中间件body-parser,在不用body-parser的情况下,获取请求体比较麻烦,还要自己解析请求体,但是使用中间件body-parser,再获取请求体就很简单。

javascirpt 复制代码
// 无body-parser的情况
app.post('/api/user', (req, res) => {
  let requestBody = '';

  req.on('data', chunk => {
    requestBody += chunk;
  });

  req.on('end', () => {
    // requestBody 包含未经解析的请求体数据
    console.log(requestBody);

    // 在这里进行自定义的请求体解析和处理逻辑
    // ...

    res.sendStatus(200);
  });
});

// 使用body-parser的情况
npm install body-parser

const bodyParser = require('body-parser');

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

app.post('/api/user', (req, res) => {
  const user = req.body;
  // 使用解析后的请求体数据进行处理
});

在Express官方文档开发中间件使用中间件中对中间件有详细介绍,想深入了解可以查看。

Express对模板引擎的支持

我们可以通过设置模板引擎和res.render方法完成对模板的支持。

javascirpt 复制代码
npm install ejs

// 设置模板引擎
app.set('view engine', 'ejs');
// 设置模板文件的存放位置
app.set('views', path.join(__dirname, 'views'));

app.get('/', (req, res) => {
  const title = 'Express with EJS';
  res.render('index', { title });
});

// views/index.ejs模板文件
<!DOCTYPE html>
<html>
  <head>
    <title>Express with EJS</title>
  </head>
  <body>
    <h1>Welcome to <%= title %></h1>
  </body>
</html>

如果想在nodejs中使用模板,代码如下:

ini 复制代码
const fs = require('fs');
const ejs = require('ejs');

const template = fs.readFileSync('./views/index.ejs', 'utf8');
const renderedHTML = ejs.render(template, { title: 'My Page' });
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(renderedHTML);
res.end();

Express怎样做错误处理

抛出错误:当您在路由处理程序或其他中间件函数中遇到错误时,可以使用next函数将错误传递给错误处理中间件。

javascirpt 复制代码
app.get('/', (req, res, next) => {
  // 模拟一个错误
  const err = new Error('Something went wrong');
  next(err);
});

// 创建一个专门用于处理错误的中间件函数,用于处理服务运行过程发生的异常
function errorHandler(err, req, res, next) {
  // 处理错误逻辑
  console.error(err);

  // 设置响应状态码
  res.status(500);
  // 发送错误响应
  res.send('Internal Server Error');
}
app.use(errorHandler);

Express怎样做静态文件服务

javascirpt 复制代码
// 指定静态文件目录
app.use(express.static('public'));
// 在页面中有静态文件的请求,express就会在public文件夹找匹配的文件进行返回,很方便。
<link rel="stylesheet" href="/css/styles.css">

Express的插件生态系统

插件生态就是利用中间件机制,可以把很多服务功能封装成中间件供开发者使用。我们以后可以做深入的研究。

写一个Koa服务

javascirpt 复制代码
const Koa = require('koa');
const app = new Koa();

// 定义路由
app.use(async (ctx) => {
  ctx.body = 'Hello, World!';
});

// 启动服务器
app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

Koa和Express有什么区别?

Koa旨在"修复和替换NodeJs",而Express则是"增强NodeJs"。

接下来我来解释这句话。

比如请求和响应这两个对象,Express仍然沿用req和res,当开发者想获取请求参数时,Express使用body-parser中间件增强了req对象,使我们可以从req.params req.query req.body获取到相应参数。对于响应方法,一个res.end稍微难用,Express增加了一些res.send res.json方法,方便开发。这就是所谓的增强NodeJs。更近一步,Express支持路由、模板等功能,都是所谓的增强。

对比Koa,处理函数的参数不再是res,req。而是ctx,内部封装了request和response两个对象,request天然就支持参数获取,response.body就设置了响应体内容。同时,可以直接在ctx对象上使用request和response两个对象的方法和属性,这就是所谓的修复和替换NodeJs。更进一步,Koa的核心库并没有路由、模板等功能,这些功能的支持都需要引入如koa-router这种额外的库,也更加复合前端渐进式的思想潮流。

洋葱模型 vs 线性模型

Koa 使用的是洋葱模型(Onion Model)的中间件处理机制。洋葱模型的核心思想是将请求和响应通过中间件层层包裹,形成一个类似洋葱的结构。每个中间件在请求进入时执行一部分逻辑,然后将控制权交给下一个中间件,最后在响应返回时逆序执行剩余的逻辑。

洋葱模型有以下几个好处:

1.简洁明了:洋葱模型将请求和响应的处理逻辑分割成多个中间件,每个中间件只关注自己的逻辑,使得代码结构清晰、易于维护。

2.可复用性:中间件是独立的功能模块,可以在不同的应用程序中复用,提高代码的可复用性和可扩展性。

3.可扩展性:通过添加、删除或修改中间件,我们可以灵活地调整请求和响应的处理流程,实现不同的功能和需求。

4.异步流程控制:洋葱模型中的每个中间件都可以使用async/await或返回 Promise 的方式处理异步操作,方便处理异步任务,例如数据库查询、网络请求等。

5.错误处理:洋葱模型在处理错误时非常方便,每个中间件可以通过try/catch捕获错误并进行相应的处理,或者将错误传递给下一个中间件进行统一的错误处理。

总的来说,洋葱模型使得 Koa 的中间件处理变得灵活、可复用,并且能够更好地控制请求和响应的流程,提高开发效率和代码质量。

Koa怎样处理路由

javascirpt 复制代码
npm install koa-router

const router = require('@koa/router');

const app = new Koa();
const router = new Router();

// 定义路由
router.get('/', async (ctx) => {
  ctx.body = 'Hello, World!';
});

中间件方面,Koa做了什么?

这里看一个koa-cors插件的使用,跟Express也没有太明显的差异。

javascirpt 复制代码
const cors = require('@koa/cors');

// 使用 koa-cors 中间件
app.use(cors());

其他方面

Koa在模板引擎、错误处理、静态文件服务方面,跟Express也大同小异,不再赘述。

总结

这篇文章跟大家一起初步认识了一下使用Nodejs写服务及其最流行的两个框架。未来我们还可以针对前端写Node服务进行更多的探索。

相关推荐
腾讯TNTWeb前端团队7 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰10 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪10 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪10 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy11 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom12 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom12 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom12 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom12 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom12 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试