安装和配置

安装

  1. 下载安装包Windows的话下载安装包直接安装就可以
  2. Linux安装
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    # 创建存放 node js 安装包的目录
    mkdir /opt/nodejs

    # 通过 xftp 把安装包上传到 /opt/nodejs 目录下

    # 解压安装包
    tar -xvf node-v20.12.0-linux-x64.tar.xz

    # 创建nodejs目录
    mkdir /usr/local/node

    # 把解压后的安装包移动到创建的目录下
    mv /opt/nodejs/node-v20.12.0-linux-x64 /usr/local/node

    # 配置环境变量
    sudo vi /etc/profile
    export M2_HOME=/usr/local/maven/apache-maven-3.9.6
    export PATH=$PATH:$JAVA_HOME/bin:$M2_HOME/bin

    # 让配置文件生效
    source /etc/profile

    # 验证
    node -v

配置

在Windows中因为Windows PowerShell默认限制了执行脚本的权限所以需要修改权限打开Windows PowerShell需要以管理员身份运行你可以通过在开始菜单搜索PowerShell然后右键点击并选择以管理员身份运行

  • 对本次会话有效

    Set-ExecutionPolicy Bypass -Scope Process

  • 对当前用户生效

    Set-ExecutionPolicy RemoteSigned -Scope CurrentUser

  • 对所有用户给生效

    Set-ExecutionPolicy RemoteSigned -Scope LocalMachine

常用命令

查看Node版本

1
npm -v

修改镜像源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 查看当前源路径
npm config get registry

# 将下包的镜像源切换为淘宝镜像源
npm config set registry=https://registry.npmmirror.com/

# 使用nrm工具管理镜像源
npm install -g nrm

# 验证
nrm --version

# 列出可用的镜像源
nrm ls

# 切换镜像源比如切换到淘宝镜像
nrm use taobao

# 查看当前使用的镜像
nrm current

查看包的版本

1
npm view hexo-cli versions

安装模块

1
2
3
4
5
6
7
8
9
10
11
# 安装并保存到dependencies
npm install 包名

# 安装特定版本
npm install 包名@版本号

# 全局安装
npm install -g 包名

# 安装到devDependencies
npm install 包名 -D

初始化项目

1
2
3
npm init <名称>
cd <名称>
npm install

卸载模块

1
npm uninstall 包名

更新模块

1
npm update 包名

运行脚本

1
npm run 脚本名称

发布模块

1
npm publish

模块化

模块化是其核心概念之一它允许你将代码分割成独立的模块以便于维护和组织Node.js 使用的是 CommonJS 模块系统这种模式下每个模块都运行在一个单独的模块作用域内也就是说在模块里定义的变量函数和对象默认都是私有的除非明确地将其导出

创建模块

⭐⭐ 在Node.js中使用module.exportsexports来导出模块接口给其他模块使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 创建一个名为 math.js 的文件用于执行基本的数学运算
function add(a, b) {
return a + b;
}

function subtract(a, b) {
return a - b;
}

// 导出这两个函数使它们可以被其他模块使用
module.exports = {
add: add,
subtract: subtract
};

导入模块

使用require()导入自定义模块时必须指定以./../开头的路径标识符如果没有指定路径标识符则 node 会把它当作内置模块或第三方模块进行加载

使用require()导入自定义模块时如果省略了文件的扩展名则 Node.js 会按顺序分别尝试加载以下的文件

  1. 按照确切的文件名进行加载
  2. 补全 js 扩展名进行加载
  3. 补全 json 扩展名进行加载
  4. 补全 .node 扩展名进行加载
  5. 加载失败终端报错

⭐⭐ 在Node.js中使用require()函数来导入模块

1
2
3
4
5
6
7
8
// 创建一个名为 app.js 的文件

//导入 math.js 模块
const math = require('./math');

//使用 math.js 模块中的函数
console.log(math.add(5, 3)); // 输出8
console.log(math.subtract(5, 3)); // 输出2

ES6 模块支持

从 Node.js v12 开始实验性地支持了ES6模块语法你可以通过.mjs扩展名或者在package.json中设置"type": "module"来启用对ES模块的支持

1
2
3
4
5
6
7
8
9
10
11
// 创建模块
// math.mjs
export function add(a, b) {
return a + b;
}

// 导入模块
// app.mjs
import { add } from './math.mjs';

console.log(add(5, 3)); // 输出8

内置模块

Node.js 提供了一系列内置模块这些模块无需安装即可直接通过require()函数使用内置模块覆盖了从文件系统访问网络通信到数据流处理等多个方面

fs 模块

fs 模块提供了一个用于与文件系统交互的 API这个模块是基于标准 POSIX 函数构建的因此在命名和功能上与它们非常相似无论你是要读取文件写入数据到文件还是操作目录fs 模块都能满足你的需求

读取文件

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
// 异步读取数据
// fs.readFile(path[, options], callback)
// 同步读取数据
// fs.readFileSync(path[, options])

// 方法参数
// path: 文件路径
// options可选:
// - encoding: 字符串指定编码格式如 'utf8'
// - flag: 字符串默认为 'r'
// callback: 回调函数 (err, data)

// 代码示例
const fs = require('fs');

// 异步读取
fs.readFile('./example.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log(data); // 打印文件内容
});

// 同步读取
try {
const data = fs.readFileSync('./example.txt', 'utf8');
console.log(data); // 打印文件内容
} catch (err) {
console.error(err);
}

写入文件

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
// 异步写入数据
// fs.writeFile(file, data[, options], callback)
// 同步写入数据
// fs.writeFileSync(file, data[, options])

// 方法参数
// file: 文件路径或文件描述符
// data: 要写入的数据可以是字符串或 Buffer
// options可选:
// - encoding: 编码格式默认 'utf8'
// - mode: 文件模式默认 0o666
// - flag: 默认 'w'

// 代码示例
const fs = require('fs');

// 异步写入
fs.writeFile('./output.txt', 'Hello, World!', (err) => {
if (err) throw err;
console.log('File saved!');
});

// 同步写入
try {
fs.writeFileSync('./output.txt', 'Hello, World!');
console.log('File saved!');
} catch (err) {
console.error(err);
}

文件状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 获取文件的状态信息
// fs.stat(path, callback)
// 使用 lstat 获取符号链接本身的信息
// fs.lstat(path, callback)

// 方法参数
// path: 文件或目录路径
// callback: 回调函数 (err, stats)stats 是一个 fs.Stats 对象

// 示例代码
const fs = require('fs');

// 使用 stat 获取信息
fs.stat('./example.txt', (err, stats) => {
if (err) throw err;
console.log(`Is file: ${stats.isFile()}`); // 判断是否是文件
});

// 使用 lstat 获取符号链接本身的信息
fs.lstat('./symlink', (err, stats) => {
if (err) throw err;
console.log(`Is symbolic link: ${stats.isSymbolicLink()}`);
});

创建目录

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
// 异步创建目录
// fs.mkdir(path[, options], callback)
// 同步创建目录
// fs.mkdirSync(path[, options])

// 方法参数
// path: 目录路径
// options可选:
// - recursive: 布尔值是否递归创建目录默认 false
// - mode: 权限默认 0o777


// 示例代码
const fs = require('fs');

// 异步创建目录
fs.mkdir('./newDir', { recursive: true }, (err) => {
if (err) throw err;
console.log('Directory created');
});

// 同步创建目录
try {
fs.mkdirSync('./newDir', { recursive: true });
console.log('Directory created');
} catch (err) {
console.error(err);
}

读取目录

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
// 异步读取目录
// fs.readdir(path[, options], callback)
// 同步读取目录
// fs.readdirSync(path[, options])

// 方法参数
// path: 目录路径
// - options可选:
// - encoding: 字符串默认 'utf8'

// 示例代码
const fs = require('fs');

// 异步读取目录
fs.readdir('./someDir', (err, files) => {
if (err) throw err;
console.log(files); // 打印目录下的文件列表
});

// 同步读取目录
try {
const files = fs.readdirSync('./someDir');
console.log(files); // 打印目录下的文件列表
} catch (err) {
console.error(err);
}

删除文件/目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 删除文件
// fs.unlink(path, callback)
// 删除目录
// fs.rmdir(path, callback)

// 方法参数
// path: 文件或目录路径
// callback: 回调函数 (err)

// 示例代码
const fs = require('fs');

// 删除文件
fs.unlink('./toBeDeleted.txt', (err) => {
if (err) throw err;
console.log('File deleted');
});

// 删除目录
fs.rmdir('./toBeDeletedDir', (err) => {
if (err) throw err;
console.log('Directory deleted');
});

重命名文件/目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 重命名文件或目录
// fs.rename(oldPath, newPath, callback)

// 方法参数
// oldPath: 原始文件或目录路径
// newPath: 新文件或目录路径
// callback: 回调函数 (err)

// 示例代码
const fs = require('fs');

// 重命名文件或目录
fs.rename('./oldName', './newName', (err) => {
if (err) throw err;
console.log('Renamed successfully');
});

流式处理

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
// 创建读取流
// fs.createReadStream(path[, options])
// 创建写入流
// fs.createWriteStream(path[, options])

// 方法参数
// path: 文件路径
// options可选:
// - flags: 字符串默认 'r'对于读流或 'w'对于写流
// - encoding: 字符串默认 null
// - fd: 文件描述符
// - mode: 整数默认 0o666

// 示例代码
const fs = require('fs');

// 创建读流
const readStream = fs.createReadStream('./largeFile.txt');
readStream.on('data', (chunk) => {
console.log(`Received ${chunk.length} bytes of data.`);
});
readStream.on('end', () => {
console.log('Finished reading the file.');
});

// 创建写流
const writeStream = fs.createWriteStream('./output.txt');
writeStream.write('Hello, world!\n');
writeStream.end();

path 模块

path 模块提供了用于处理和转换文件路径的实用工具无论你是在 WindowsLinux 还是 macOS 上运行 Node.jspath 模块都能确保你的代码在不同操作系统之间保持一致的行为

文件路径

1
2
3
4
5
console.log(__dirname);

// 返回给定路径的目录名部分
const path = require('path');
console.log(path.dirname('/foo/bar/baz/asdf/quux.html')); // 输出: '/foo/bar/baz/asdf'

文件扩展名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// path.basename(path[, ext])
// path: 文件路径
// ext可选: 文件扩展名如果提供则返回的文件名将不包含此扩展名

// 示例代码
const path = require('path');
console.log(path.basename('/foo/bar/baz/asdf/quux.html')); // 输出: 'quux.html'
console.log(path.basename('/foo/bar/baz/asdf/quux.html', '.html')); // 输出: 'quux'

// path.extname(path)
// path: 文件路径

// 示例代码
const path = require('path');
console.log(path.extname('index.html')); // 输出: '.html'
console.log(path.extname('index.')); // 输出: '.'
console.log(path.extname('index')); // 输出: ''

拼接路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 使用平台特定的分隔符连接所有路径片段并规范化生成的路径
// path.join([...paths])
// ...paths: 路径片段数组

// 示例代码
const path = require('path');
console.log(path.join('/foo', 'bar', 'baz/asdf', 'quux', '..')); // 输出: '/foo/bar/baz/asdf'

// 规范化给定的路径解析 '..' 和 '.' 等特殊段落
// path.normalize(path)
// path: 文件路径

// 示例代码
const path = require('path');
console.log(path.normalize('/foo/bar//baz/asdf/quux/..')); // 输出: '/foo/bar/baz/asdf'

格式化路径

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
// 根据提供的对象格式化一个路径字符串
// path.format(pathObject)
// pathObject: 包含路径组成部分的对象如 dir, root, base, name, ext

// 示例代码
const path = require('path');
console.log(path.format({
dir: '/home/user/dir',
base: 'file.txt'
})); // 输出: '/home/user/dir/file.txt'

// 解析给定路径并返回一个对象包含 root, dir, base, name, ext 字段
// path.parse(path)
// path: 文件路径

// 示例代码
const path = require('path');
console.log(path.parse('/home/user/dir/file.txt'));
/* 输出:
{
root: '/',
dir: '/home/user/dir',
base: 'file.txt',
ext: '.txt',
name: 'file'
}
*/

相对路径

1
2
3
4
5
6
7
8
9
// 计算从起始路径到目标路径的相对路径
// path.relative(from, to)
// from: 起始路径
// to: 目标路径

// 示例代码
const path = require('path');
console.log(path.relative('/data/orandea/test/aaa', '/data/orandea/impl/bbb'));
// 输出: '../impl/bbb'

绝对路径

1
2
3
4
5
6
7
8
// 将路径或路径片段序列解析为绝对路径
// path.resolve([...paths])
// ...paths: 路径片段数组

// 示例代码
const path = require('path');
console.log(path.resolve('/foo/bar', './baz')); // 输出: '/foo/bar/baz'
console.log(path.resolve('/foo/bar', '/tmp/file/')); // 输出: '/tmp/file'

路径分隔符

1
2
3
4
5
6
// 返回当前操作系统的路径分隔符Windows 上是 \其他大多数系统上是 /
// path.sep

// 示例代码
const path = require('path');
console.log(path.sep); // 在 POSIX 上输出 '/', 在 Windows 上输出 '\'

http 模块

http 模块提供了一个用于创建 HTTP 服务器和客户端的接口使得你可以轻松地在 Node.js 应用中处理 HTTP 请求和响应

创建HTTP服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 创建 HTTP 服务器
// http.createServer([requestListener])
// requestListener: 可选的回调函数当接收到请求时调用该回调函数接收两个参数request 和 response 对象

// 示例代码
const http = require('http');

// 创建一个 HTTP 服务器
const server = http.createServer((req, res) => {
console.log('Request received');
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello, World!\n');
});

// 监听端口
server.listen(3000, () => {
console.log('Server running at http://localhost:3000/');
});

发送HTTP请求

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
// 发送 HTTP 请求
// http.request(options[, callback])
// options: 配置对象包括 hostname, port, path, method, headers 等
// callback: 可选的回调函数当接收到响应时调用

// 示例代码
const http = require('http');

// 配置选项
const options = {
hostname: 'www.example.com',
port: 80,
path: '/',
method: 'GET'
};

// 发出请求
const req = http.request(options, (res) => {
let data = '';

// 接收数据
res.on('data', (chunk) => {
data += chunk;
});

// 数据接收完毕
res.on('end', () => {
console.log(data);
});
});

// 错误处理
req.on('error', (e) => {
console.error(`Problem with request: ${e.message}`);
});

// 结束请求
req.end();

发送GET请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 简化版的 http.request()用于发出 GET 请求并自动调用 req.end()
// http.get(options[, callback])

// 示例代码
const http = require('http');

http.get('http://www.example.com/', (res) => {
let data = '';

// 接收数据
res.on('data', (chunk) => {
data += chunk;
});

// 数据接收完毕
res.on('end', () => {
console.log(data);
});

}).on('error', (e) => {
console.error(`Got error: ${e.message}`);
});

模块常量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// http模块常量
const http = require('http');

// 返回一个数组包含所有支持的 HTTP 方法如 'GET', 'POST', 'PUT' 等
console.log(http.METHODS);

// 返回一个对象包含所有标准 HTTP 状态码及其描述
console.log(http.STATUS_CODES[404]);

// 请求对象 (req)
// req.method: 请求方法如 'GET', 'POST'
// req.url: 请求的 URL 路径
// req.headers: 请求头信息的对象
// req.on('data', callback): 当有数据块到达时触发
// req.on('end', callback): 所有数据接收完毕后触发

// 响应对象 (res)
// res.writeHead(statusCode[, statusMessage][, headers]): 设置响应状态码和头部信息
// res.write(chunk[, encoding]): 向响应流写入数据块
// res.end([data][, encoding]): 完成响应并可选地发送数据

os 模块

获取操作系统信息

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
const os = require('os');

// 获取操作系统的 CPU 架构
console.log(os.arch()); // 输出: 'x64' 或 'arm64' 等

// 获取操作系统平台标识符
console.log(os.platform()); // 输出: 'darwin', 'linux', 'win32' 等

// 获取操作系统名称
console.log(os.type()); // 输出: 'Darwin', 'Linux', 'Windows_NT' 等

// 获取操作系统的发行版本
console.log(os.release()); // 输出: '19.6.0' 或其他版本号

// 获取系统负载返回一个包含最近 1 分钟5 分钟和 15 分钟系统平均负载的数组
console.log(os.loadavg()); // 输出: [0.23, 0.45, 0.56]

// 获取操作系统的临时文件目录
console.log(os.tmpdir()); // 输出: '/tmp' 或 'C:\Users\...\AppData\Local\Temp'

// 获取系统运行时间
console.log(`${os.uptime()} seconds`); // 输出: '3600 seconds'

// 获取当前计算机的主机名
console.log(os.hostname()); // 输出: 'my-computer-name'

// 获取用户信息
console.log(os.userInfo());
/*
输出类似:
{
uid: -1,
gid: -1,
username: 'username',
homedir: '/Users/username',
shell: ''
}
*/

获取CPU信息

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
const os = require('os');

// 获取系统总内存大小字节
console.log(`${os.totalmem() / (1024 * 1024 * 1024)} GB`); // 输出: '16 GB'

// 获取系统可用内存大小字节
console.log(`${os.freemem() / (1024 * 1024 * 1024)} GB`); // 输出: '8 GB'

// 获取CPU 内核信息返回一个数组每个元素代表一个逻辑 CPU 内核的信息
console.log(os.cpus());
/*
输出类似:
[
{
model: 'Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz',
speed: 2808,
times: {
user: 1556720,
nice: 0,
sys: 157840,
idle: 1321840,
irq: 0
}
},
...
]
*/

获取网络接口信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const os = require('os');
console.log(os.networkInterfaces());
/*
输出类似:
{
en0: [
{
address: 'fe80::1',
netmask: 'ffff:ffff:ffff:ffff::',
family: 'IPv6',
mac: '00:00:00:00:00:00',
internal: true,
cidr: 'fe80::1/64'
},
...
],
...
}
*/

url 模块

url 模块提供了用于解析和处理 URL 的实用工具这个模块非常有用尤其是在你需要处理复杂的 URL 字符串构建新的 URL 或者对现有 URL 进行操作时

解析 URL

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
// 可以将一个 URL 字符串解析为一个对象
// new URL(url_path)
// url_pathurl地址

//示例代码
const { URL } = require('url');

const urlString = 'https://user:pass@sub.host.com:8080/p/a/t/h?query=string#hash';
const parsedUrl = new URL(urlString);

console.log(parsedUrl);
/*
输出:
URL {
href: 'https://user:pass@sub.host.com:8080/p/a/t/h?query=string#hash',
origin: 'https://sub.host.com:8080',
protocol: 'https:',
username: 'user',
password: 'pass',
host: 'sub.host.com:8080',
hostname: 'sub.host.com',
port: '8080',
pathname: '/p/a/t/h',
search: '?query=string',
searchParams: URLSearchParams { 'query' => 'string' },
hash: '#hash'
}
*/

序列化 URL 对象

1
2
3
4
5
6
7
8
// 将一个 URL 对象转换回字符串形式
// url.href

// 示例代码
const { URL } = require('url');

const url = new URL('https://user:pass@sub.host.com:8080/p/a/t/h?query=string#hash');
console.log(url.href); // 输出: 'https://user:pass@sub.host.com:8080/p/a/t/h?query=string#hash'

处理查询参数

1
2
3
4
5
6
7
8
9
10
// URLSearchParams 接口提供了一种简单的方式来处理查询字符串

// 示例代码
const { URLSearchParams } = require('url');

const params = new URLSearchParams('foo=bar&abc=xyz');
console.log(params.get('foo')); // 输出: 'bar'

params.append('foo', 'baz');
console.log(params.toString()); // 输出: 'foo=bar&abc=xyz&foo=baz'

相对 URL 转换

1
2
3
4
5
6
7
8
9
10
11
// 根据一个基础 URL 和相对路径生成一个新的绝对 URL
// url.resolve()

// 示例代码
const url = require('url');

const baseUrl = 'http://example.com/path/';
const relativeUrl = './other/path';

const resolvedUrl = url.resolve(baseUrl, relativeUrl);
console.log(resolvedUrl); // 输出: 'http://example.com/path/other/path'

zlib 模块

zlib 模块提供了压缩和解压缩功能支持 Gzip 和 Deflate 压缩算法适用于多种应用场景如网络传输中的数据压缩日志文件的压缩存储等

创建压缩流

⭐⭐ Gzip 压缩

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//Gzip 是一种广泛使用的压缩格式尤其在网络传输中非常常见

const fs = require('fs');
const zlib = require('zlib');

// 创建读取流和写入流
const gzip = zlib.createGzip();
const inFile = fs.createReadStream('input.txt');
const outFile = fs.createWriteStream('input.txt.gz');

// 将输入流通过gzip压缩后写入输出流
inFile.pipe(gzip).pipe(outFile);

console.log('文件已成功压缩为 Gzip 格式');

⭐⭐ Deflate 压缩

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Deflate 是另一种常见的压缩算法通常比 Gzip 更快但压缩率稍低

const fs = require('fs');
const zlib = require('zlib');

// 创建读取流和写入流
const deflate = zlib.createDeflate();
const inFile = fs.createReadStream('input.txt');
const outFile = fs.createWriteStream('input.txt.deflate');

// 将输入流通过deflate压缩后写入输出流
inFile.pipe(deflate).pipe(outFile);

console.log('文件已成功压缩为 Deflate 格式');

解压缩流

⭐⭐ Gzip 解压缩

1
2
3
4
5
6
7
8
9
10
11
12
const fs = require('fs');
const zlib = require('zlib');

// 创建读取流和写入流
const gunzip = zlib.createGunzip();
const inFile = fs.createReadStream('input.txt.gz');
const outFile = fs.createWriteStream('output.txt');

// 将输入流通过gunzip解压缩后写入输出流
inFile.pipe(gunzip).pipe(outFile);

console.log('文件已成功解压缩');

⭐⭐ Deflate 解压缩

1
2
3
4
5
6
7
8
9
10
11
12
const fs = require('fs');
const zlib = require('zlib');

// 创建读取流和写入流
const inflate = zlib.createInflate();
const inFile = fs.createReadStream('input.txt.deflate');
const outFile = fs.createWriteStream('output.txt');

// 将输入流通过inflate解压缩后写入输出流
inFile.pipe(inflate).pipe(outFile);

console.log('文件已成功解压缩');

同步压缩与解压缩

除了流处理zlib 模块还提供了同步方法来进行一次性压缩和解压缩操作

⭐⭐ 同步压缩数据

1
2
3
4
5
6
const zlib = require('zlib');

const data = 'This is a sample string to be compressed';
const compressedData = zlib.deflateSync(Buffer.from(data));

console.log('压缩后的数据:', compressedData.toString('base64'));

⭐⭐ 同步解压缩数据

1
2
3
4
5
6
const zlib = require('zlib');

const compressedData = Buffer.from('压缩后的数据', 'base64');
const decompressedData = zlib.inflateSync(compressedData);

console.log('解压缩后的数据:', decompressedData.toString());

高级选项

zlib 提供了一些高级选项来调整压缩行为例如设置压缩级别内存窗口大小等

⭐⭐ 设置压缩级别

1
2
3
4
5
6
7
8
9
// 压缩级别决定了压缩的速度和效率范围从 1最快到 9最高压缩率默认值为 6
const zlib = require('zlib');

const data = 'This is a sample string to be compressed';

// 使用压缩级别 9 进行压缩
const compressedData = zlib.deflateSync(Buffer.from(data), { level: 9 });

console.log('压缩后的数据:', compressedData.toString('base64'));

⭐⭐ 设置内存窗口大小

1
2
3
4
5
6
7
8
9
// 内存窗口大小决定了压缩过程中使用的内存缓冲区大小默认值为 15 (32K)
const zlib = require('zlib');

const data = 'This is a sample string to be compressed';

// 使用内存窗口大小 16 (64K) 进行压缩
const compressedData = zlib.deflateSync(Buffer.from(data), { windowBits: 16 + 15 });

console.log('压缩后的数据:', compressedData.toString('base64'));

crypto 模块

crypto 模块提供了加密功能包括哈希HMAC加密解密签名和验证等这个模块非常强大且灵活适用于需要处理敏感数据的应用场景如密码存储数据加密传输等

哈希

哈希函数将任意长度的数据映射为固定长度的输出常用于数据完整性校验和密码存储

1
2
3
4
5
6
7
8
9
10
11
const crypto = require('crypto');

// 创建一个SHA-256哈希实例
// 常见的哈希算法包括'sha256''sha512''md5''ripemd160'
const hash = crypto.createHash('sha256');

// 更新哈希内容
hash.update('Some data to hash');

// 获取最终的哈希值
console.log(hash.digest('hex')); // 输出: 'a94a8fe5ccb19ba61c4c0873d391e987982fbbd3'

HMAC

HMAC 是一种基于哈希的消息认证码通常用于验证消息的完整性和真实性

1
2
3
4
5
6
7
8
9
10
const crypto = require('crypto');

// 创建一个HMAC-SHA256实例
const hmac = crypto.createHmac('sha256', 'secret-key');

// 更新HMAC内容
hmac.update('Some data to hash');

// 获取最终的HMAC值
console.log(hmac.digest('hex')); // 输出: 'f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8'

加密/解密

crypto 模块支持多种对称加密算法如 AES可以用来加密和解密数据

⭐⭐ 对称加密

1
2
3
4
5
6
7
8
9
10
11
12
13
const crypto = require('crypto');

// 生成随机密钥和初始化向量IV
const key = crypto.randomBytes(32); // AES-256 需要32字节的密钥
const iv = crypto.randomBytes(16); // IV 需要16字节

// 创建加密器
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);

let encrypted = cipher.update('Some secret message', 'utf8', 'hex');
encrypted += cipher.final('hex');

console.log(`加密后的数据: ${encrypted}`);

⭐⭐ 对称解密

1
2
3
4
5
6
7
8
9
const crypto = require('crypto');

// 使用相同的密钥和IV进行解密
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);

let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');

console.log(`解密后的数据: ${decrypted}`); // 输出: 'Some secret message'

公钥/私钥对

crypto 模块支持生成公钥/私钥对并提供签名和验证功能

⭐⭐ 生成密钥对

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

// 生成RSA密钥对
crypto.generateKeyPair('rsa', {
modulusLength: 2048,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
cipher: 'aes-256-cbc',
passphrase: 'top secret'
}
}, (err, publicKey, privateKey) => {
if (err) throw err;
console.log(`公钥:\n${publicKey}`);
console.log(`私钥:\n${privateKey}`);
});

⭐⭐ 签名和验证

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

// 使用私钥签名
const sign = crypto.createSign('RSA-SHA256');
sign.update('Some data to sign');
sign.end();

const signature = sign.sign(privateKey, 'hex');
console.log(`签名: ${signature}`);

// 使用公钥验证签名
const verify = crypto.createVerify('RSA-SHA256');
verify.update('Some data to sign');
verify.end();

const isVerified = verify.verify(publicKey, signature, 'hex');
console.log(`验证结果: ${isVerified}`); // 输出: true 或 false

密钥派生函数

crypto 模块支持 PBKDF2Password-Based Key Derivation Function 2等密钥派生函数用于从密码生成密钥

1
2
3
4
5
6
7
8
9
10
11
12
const crypto = require('crypto');

const password = 'somepassword';
const salt = crypto.randomBytes(64);
const iterations = 10000;
const keylen = 32; // 生成32字节的密钥
const digest = 'sha256';

crypto.pbkdf2(password, salt, iterations, keylen, digest, (err, derivedKey) => {
if (err) throw err;
console.log(`派生密钥: ${derivedKey.toString('hex')}`);
});

生成随机数

crypto 模块提供了安全的随机数生成函数适用于生成密钥IV 和其他需要高熵值的场合

1
2
3
4
5
const crypto = require('crypto');

// 生成32字节的随机数据
const randomBytes = crypto.randomBytes(32);
console.log(`随机字节: ${randomBytes.toString('hex')}`);

stream 模块

stream 模块是处理流数据的核心工具它允许你以高效的方式处理大量数据而无需一次性将所有数据加载到内存中

Stream是一种抽象接口用于表示能顺序读取或写入的数据源Node.js 提供了多种类型的流包括可读流Readable可写流Writable双工流Duplex和转换流Transform

流的类型

⭐⭐ 可读流从某个来源读取数据的流常见的例子包括从文件系统读取文件从网络连接读取数据等

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
const { Readable } = require('stream');

class MyReadable extends Readable {
constructor(options) {
super(options);
this.index = 1;
}

_read(size) {
const chunk = `${this.index++}\n`;
if (this.index > 5) {
this.push(null); // 结束流
} else {
this.push(chunk);
}
}
}

const myReadable = new MyReadable();
myReadable.on('data', (chunk) => {
console.log(`接收到数据: ${chunk.toString()}`);
});
myReadable.on('end', () => {
console.log('数据传输结束');
});

⭐⭐ 可写流向某个目的地写入数据的流常见的例子包括写入文件系统发送数据到网络连接等

1
2
3
4
5
6
7
8
9
10
11
12
13
const { Writable } = require('stream');

class MyWritable extends Writable {
_write(chunk, encoding, callback) {
console.log(`写入数据: ${chunk.toString()}`);
callback(); // 完成写入操作
}
}

const myWritable = new MyWritable();
myWritable.write('Hello ');
myWritable.write('World!\n');
myWritable.end();

⭐⭐ 双工流同时实现了可读和可写的接口可以同时从某个来源读取数据并写入另一个目的地

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const { Duplex } = require('stream');

class MyDuplex extends Duplex {
_write(chunk, encoding, callback) {
console.log(`写入数据: ${chunk.toString()}`);
callback();
}

_read(size) {
const chunk = 'Some data\n';
this.push(chunk);
this.push(null); // 结束流
}
}

const myDuplex = new MyDuplex();
myDuplex.on('data', (chunk) => {
console.log(`接收到数据: ${chunk.toString()}`);
});

myDuplex.write('Hello ');
myDuplex.write('World!\n');
myDuplex.end();

⭐⭐ 转换流是一种特殊的双工流它可以对输入数据进行某种形式的转换并将其作为输出数据常见的例子包括加密压缩解密解压缩等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const { Transform } = require('stream');

class MyTransform extends Transform {
_transform(chunk, encoding, callback) {
const transformedChunk = chunk.toString().toUpperCase();
this.push(transformedChunk);
callback();
}
}

const myTransform = new MyTransform();
myTransform.on('data', (chunk) => {
console.log(`转换后的数据: ${chunk.toString()}`);
});

myTransform.write('hello world\n');
myTransform.end();

流的管道

流的一个强大特性是能够通过管道piping将多个流串联起来这样可以轻松地构建复杂的数据处理流水线

⭐⭐ 将可读流通过转换流再写入可写流

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
const { Readable, Writable, Transform } = require('stream');

// 可读流
const readable = new Readable({
read() {
this.push('Hello World!\n');
this.push(null); // 结束流
}
});

// 转换流
const transform = new Transform({
transform(chunk, encoding, callback) {
const transformedChunk = chunk.toString().toUpperCase();
this.push(transformedChunk);
callback();
}
});

// 可写流
const writable = new Writable({
write(chunk, encoding, callback) {
console.log(`写入数据: ${chunk.toString()}`);
callback();
}
});

// 管道连接
readable.pipe(transform).pipe(writable);

流的事件

流对象会触发各种事件你可以监听这些事件来处理特定的情况

  • ‘data’: 当有数据可读时触发
  • ‘end’: 当没有更多数据可读时触发
  • ‘error’: 当发生错误时触发
  • ‘finish’: 当所有数据已写入底层系统时触发
  • ‘close’: 当流或其底层资源被关闭时触发

⭐⭐ 监听流的事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const { Readable } = require('stream');

const readable = new Readable({
read() {
this.push('Hello ');
this.push('World!\n');
this.push(null); // 结束流
}
});

readable.on('data', (chunk) => {
console.log(`接收到数据: ${chunk.toString()}`);
});

readable.on('end', () => {
console.log('数据传输结束');
});

readable.on('error', (err) => {
console.error(`发生错误: ${err.message}`);
});

常用方法

⭐⭐ pipeline 是一种更安全的方式来连接多个流并确保它们在出现错误时正确关闭

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const { pipeline } = require('stream');
const fs = require('fs');
const zlib = require('zlib');

// 使用pipeline连接多个流
pipeline(
fs.createReadStream('input.txt'),
zlib.createGzip(),
fs.createWriteStream('input.txt.gz'),
(err) => {
if (err) {
console.error('管道出错:', err.message);
} else {
console.log('文件已成功压缩为 Gzip 格式');
}
}
);

⭐⭐ finished 方法用于等待流的所有操作完成包括数据读取写入和错误处理

1
2
3
4
5
6
7
8
9
10
11
12
13
const { finished } = require('stream');
const fs = require('fs');

const rs = fs.createReadStream('input.txt');
finished(rs, (err) => {
if (err) {
console.error('流操作出错:', err.message);
} else {
console.log('流操作完成');
}
});

rs.resume(); // 开始读取流

其他模块

子进程

用于创建子进程允许你在 Node.js 应用中执行外部命令或脚本并与它们进行通信

1
2
3
4
5
6
7
8
9
10
11
12
13
// child_process.spawn(): 启动一个新的进程
// child_process.exec(): 执行命令并缓冲输出
// child_process.execFile(): 类似于 exec()但只执行指定的文件
// child_process.fork(): 创建一个新进程并运行指定的模块

const { exec } = require('child_process');
exec('ls -lh', (err, stdout, stderr) => {
if (err) {
console.error(`执行出错: ${stderr}`);
return;
}
console.log(`结果: ${stdout}`);
});

字符串处理

1
2
3
4
5
6
7
8
9
10
11
const querystring = require('querystring');

// 转义字符串
const unescaped = 'Hello World!';
const escaped = querystring.escape(unescaped);
console.log(escaped); // 输出: 'Hello%20World%21'

// 反转义字符串
const escaped2 = 'Hello%20World%21';
const unescaped2 = querystring.unescape(escaped2);
console.log(unescaped2); // 输出: 'Hello World!'

逐行读取输入流

用于逐行读取输入流适合处理交互式命令行应用

1
2
3
4
5
6
7
8
9
10
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});

rl.question('你叫什么名字? ', (answer) => {
console.log(`你好, ${answer}!`);
rl.close();
});

vm 模块

用于在虚拟机环境中运行 JavaScript 代码适用于沙箱环境下的代码执行

1
2
3
4
5
6
7
8
9
const vm = require('vm');

const script = new vm.Script('globalVar = "set by script"');

const sandbox = {};
vm.createContext(sandbox); // 创建新的上下文
script.runInContext(sandbox); // 在当前上下文中运行代码

console.log(sandbox.globalVar); // 输出: 'set by script'

dns 模块

用于解析域名系统DNS支持同步和异步查询

1
2
3
4
5
6
7
8
// dns.resolve(): 异步解析域名
// dns.lookup(): 查找主机名对应的 IP 地址
// dns.getServers(): 获取当前使用的 DNS 服务器列表

const dns = require('dns');
dns.lookup('example.com', (err, address, family) => {
console.log(`地址: ${address} 家族: IPv${family}`);
});

tls 模块

提供传输层安全TLS和安全套接字层SSL协议的支持用于创建安全的网络连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// tls.createServer(): 创建 TLS 服务器
// tls.connect(): 创建 TLS 客户端连接
const tls = require('tls');
const fs = require('fs');

const options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem')
};

const server = tls.createServer(options, (socket) => {
socket.write('secure hello');
socket.end();
});

server.listen(8000, () => {
console.log('服务器正在监听端口 8000');
});

events 模块

事件触发器模块提供了事件驱动编程的基本机制

1
2
3
4
5
6
7
8
9
10
11
// EventEmitter: 核心类用于绑定和触发事件
const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
console.log('事件触发!');
});

myEmitter.emit('event'); // 触发事件

Express

Express 是 Node.js 上最流行的 WEB 应用框架之一属于第三方模块用于快速构建可扩展的网络应用和服务它是基于内置的 http 模块进一步封装出来的

安装

1
2
3
4
# 安装最新版
npm i express
# 安装特定版本
npm i express@版本号

使用

1
2
3
4
5
6
7
8
9
10
11
const express = require('express');
const app = express();

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

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器正在监听端口 ${PORT}`);
});

路由

路由是指如何定义应用响应不同 URL 的方式Express 提供了强大的路由功能允许你定义各种 HTTP 方法如 GETPOST 等的处理函数

基本路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
app.get('/', (req, res) => {
res.send('GET 请求到主页');
});

app.post('/', (req, res) => {
res.send('POST 请求到主页');
});

app.put('/user', (req, res) => {
res.send('PUT 请求到用户页面');
});

app.delete('/user', (req, res) => {
res.send('DELETE 请求到用户页面');
});

动态路由

1
2
3
4
// 动态路由允许你在 URL 中包含参数
app.get('/users/:userId/books/:bookId', (req, res) => {
res.send(req.params); // 输出: { userId: '12345', bookId: '6789' }
});

链式调用

1
2
3
4
5
6
7
8
9
10
app.route('/book')
.get((req, res) => {
res.send('获取图书信息');
})
.post((req, res) => {
res.send('添加新书');
})
.put((req, res) => {
res.send('更新图书信息');
});

模块化路由

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
// 假设你有一个复杂的应用希望将不同的路由分组管理可以创建一个单独的路由文件 users.js
const express = require('express');
const router = express.Router();

router.get('/user/', (req, res) => {
res.send('用户列表');
});

router.get('user/:id', (req, res) => {
res.send(`用户信息: ${req.params.id}`);
});

module.exports = router;

// 然后在主应用文件中使用这个路由
const express = require('express');
const app = express();
const usersRouter = require('./users');

app.use(usersRouter);
//可以添加访问前缀
//app.use("/api", usersRouter);

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器正在监听端口 ${PORT}`);
});

中间件

中间件是 Express 应用的核心部分它是一些可以访问请求对象req响应对象res以及应用请求-响应周期中的下一个中间件函数的函数

使用app.use()函数注册全局中间件要放在所有路由之前注册

内置中间件

⭐⭐ req.query解析查询字符串可以直接使用不用注册

1
2
3
4
5
6
7
8
9
10
11
12
const express = require('express');
const app = express();

app.get('/search', (req, res) => {
const query = req.query;
res.send(`查询参数: ${JSON.stringify(query)}`);
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器正在监听端口 ${PORT}`);
});

⭐⭐ express.static用于提供静态文件服务的中间件

1
2
3
4
app.use(express.static('public'));//一个静态资源
app.use(express.static('others'));//多个静态资源
//可以添加访问前缀
app.use('/uploads', express.static('uploads'));

⭐⭐ express.json用于解析 JSON 格式的请求体的中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const express = require('express');
const app = express();

// 解析 application/json 请求体
app.use(express.json());

app.post('/profile', (req, res) => {
console.log(req.body); // 输出请求体中的 JSON 数据
res.send('数据已接收');
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器正在监听端口 ${PORT}`);
});

⭐⭐ express.urlencoded用于解析 URL 编码格式的请求体的中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const express = require('express');
const app = express();

// 解析 application/x-www-form-urlencoded 请求体
// extended是否使用第三方解析
app.use(express.urlencoded({ extended: false }));

app.post('/login', (req, res) => {
console.log(req.body); // 输出请求体中的表单数据
res.send('登录成功');
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器正在监听端口 ${PORT}`);
});

第三方中间件

⭐⭐ body-parser 用于解析请求体中的数据

1
2
3
4
# Body Parser (已集成到 Express 4.16.0+)
# Express 4.16.0 之后版本可以使用express.json 中间件
# Express 4.16.0 之前版本需要安装body-parser 中间件
npm install body-parser
1
2
3
4
5
6
7
8
9
10
11
12
const bodyParser = require('body-parser');

// 解析 application/json
app.use(bodyParser.json());

// 解析 application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));

app.post('/login', (req, res) => {
console.log(req.body); // 输出请求体中的表单数据
res.send('登录成功');
});

⭐⭐ Helmet用户设置各种 HTTP 头以保护应用的安全中间件

1
npm install helmet
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
const express = require('express');
const helmet = require('helmet');
const app = express();

// 使用默认配置
app.use(helmet());

// 或者自定义配置
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"]
}
}
}));

app.get('/', (req, res) => {
res.send('这是一个安全的应用');
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器正在监听端口 ${PORT}`);
});

⭐⭐ morgan可以将每个请求的日志输出到终端或文件

1
npm install morgan
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const express = require('express');
const morgan = require('morgan');
const fs = require('fs');
const path = require('path');
const app = express();

// 基本使用输出到终端
app.use(morgan('dev'));

// 高级使用输出到文件
const accessLogStream = fs.createWriteStream(path.join(__dirname, 'access.log'), { flags: 'a' });
app.use(morgan('combined', { stream: accessLogStream }));

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

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器正在监听端口 ${PORT}`);
});

⭐⭐ compression用于压缩响应内容减少传输的数据量从而加快页面加载速度

1
npm install compression
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const express = require('express');
const compression = require('compression');
const app = express();

// 使用默认配置
app.use(compression());

app.get('/', (req, res) => {
res.send('<html><body><h1>这是一个压缩的响应</h1></body></html>');
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器正在监听端口 ${PORT}`);
});

⭐⭐ express-session用于管理用户的会话状态通常与身份验证一起使用

1
npm install express-session
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
const express = require('express');
const session = require('express-session');
const app = express();

// 配置会话中间件
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
cookie: { secure: false } // 设置为 true 如果使用 HTTPS
}));

app.get('/', (req, res) => {
if (req.session.views) {
req.session.views++;
res.setHeader('Content-Type', 'text/html');
res.write('<p>views: ' + req.session.views + '</p>');
res.end();
} else {
req.session.views = 1;
res.end('欢迎首次访问');
}
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器正在监听端口 ${PORT}`);
});

⭐⭐ multer用于处理 multipart/form-data 类型表单数据的中间件主要用于文件上传

1
npm install multer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const express = require('express');
const multer = require('multer');
const upload = multer({ dest: 'uploads/' }); // 文件将被存储到 uploads/ 目录下
const app = express();

app.post('/upload', upload.single('file'), (req, res) => {
console.log(req.file); // 上传的文件信息
console.log(req.body); // 其他表单字段
res.send('文件已上传');
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器正在监听端口 ${PORT}`);
});

⭐⭐ express-rate-limit一个简单的限流中间件用于限制客户端的请求速率防止恶意攻击

1
npm install express-rate-limit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const express = require('express');
const rateLimit = require('express-rate-limit');
const app = express();

// 创建限流器
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100 // 每个IP最多100次请求
});

// 应用限流器
app.use(limiter);

app.get('/', (req, res) => {
res.send('你可以每15分钟发送100次请求');
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器正在监听端口 ${PORT}`);
});

⭐⭐ serve-favicon用于提供网站的 favicon.ico 文件

1
npm install serve-favicon
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const express = require('express');
const favicon = require('serve-favicon');
const path = require('path');
const app = express();

// 提供favicon
app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));

app.get('/', (req, res) => {
res.send('首页');
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器正在监听端口 ${PORT}`);
});

⭐⭐ CORS (跨域资源共享)

1
npm install cors
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const express = require('express');
const cors = require('cors');
const app = express();

// 允许所有来源的跨域请求
app.use(cors());

// 或者配置特定的源
// const corsOptions = {
// origin: 'http://example.com',
// optionsSuccessStatus: 200 // 一些旧浏览器需要这个
// };
// app.use(cors(corsOptions));

app.get('/data', (req, res) => {
res.json({ message: '这是跨域资源' });
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器正在监听端口 ${PORT}`);
});

⭐⭐ CSRF (跨站请求伪造防护)

1
npm install csurf
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
const express = require('express');
const cookieParser = require('cookie-parser');
const csrf = require('csurf');
const bodyParser = require('body-parser');
const app = express();

// 解析 cookies 和 body
app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: false }));

// 使用 CSRF 中间件
const csrfProtection = csrf({ cookie: true });
app.get('/form', csrfProtection, (req, res) => {
res.send(`<form method="POST" action="/process">${csrfTokenInput(req)}</form>`);
});

function csrfTokenInput(req) {
return `<input type="hidden" name="_csrf" value="${req.csrfToken()}" />`;
}

app.post('/process', csrfProtection, (req, res) => {
res.send('数据已处理');
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器正在监听端口 ${PORT}`);
});

自定义中间件

自定义中间件必须有三个参数(req, res, next)要放在所有路由之前你可以使用它来处理特定的逻辑

⭐⭐ 全局生效

1
2
3
4
app.use((req, res, next) => {
console.log("全局生效");
next(); // 调用下一个中间件
});

⭐⭐ 局部生效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const jb1 = (req, res, next) => {
console.log("局部生效1");
next(); // 调用下一个中间件
};

const jb2 = (req, res, next) => {
console.log("局部生效2");
next(); // 调用下一个中间件
};

// 第一种写法
app.get('/', jb1, jb2, (req, res) => {
res.send('数据已处理');
});

// 第二种写法
app.get('/', [jb1, jb2], (req, res) => {
res.send('数据已处理');
});

错误处理中间件

错误处理中间件必须有四个参数(err, req, res, next)要放在所有路由之后你可以使用它来捕获并处理应用中的错误

1
2
3
4
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('服务器发生错误');
});

Session

express-session 是一个用于管理用户会话的中间件广泛应用于基于 Express 的应用中它通过在服务器端存储会话数据并在客户端设置一个会话标识符通常是通过 cookie来跟踪用户的会话状态

安装

1
npm install express-session

配置

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
const express = require('express');
const session = require('express-session');
const app = express();

// 配置 session 中间件
// 配置选项:
// secret: 必需用于对会话 ID 进行签名的字符串或字符串数组
// name: 可选会话 cookie 的名称默认为 'connect.sid'
// store: 可选用于存储会话数据的存储器默认是内存存储器建议在生产环境中使用持久化存储
// cookie: 可选用于配置会话 cookie 的属性如 maxAge, secure, httpOnly 等
// resave: 可选是否强制在每次请求时将未修改的会话重新保存到存储中默认为 true
// saveUninitialized: 可选是否自动保存未初始化的会话默认为 true
app.use(session({
secret: 'keyboard cat', // 用于签名 session ID 的密钥
resave: false, // 强制会话保存回存储
saveUninitialized: true // 初始化未修改的会话
}));

app.get('/', (req, res) => {
if (req.session.views) {
req.session.views++;
res.setHeader('Content-Type', 'text/html');
res.write('<p>views: ' + req.session.views + '</p>');
res.end();
} else {
req.session.views = 1;
res.end('欢迎首次访问');
}
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器正在监听端口 ${PORT}`);
});

存储器

默认情况下express-session 使用内存存储器来保存会话数据然而在生产环境中建议使用持久化存储器以确保会话数据不会丢失常见的存储器实现包括MemoryStore不推荐用于生产环境RedisStoreMongoDBStore

1
npm install redis connect-redis
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
42
43
44
45
46
47
48
const express = require('express');
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
const redis = require('redis');

const redisClient = redis.createClient({
url: 'redis://localhost:6379'
});

redisClient.connect().then(() => {
const app = express();

// 配置 session 中间件
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
// cookie 配置
// maxAge: 设置 cookie 的最大生存时间毫秒例如maxAge: 60000 表示 cookie 在创建后 60 秒过期
// secure: 设置为 true 时仅在 HTTPS 请求中发送 cookie默认为 false
// httpOnly: 设置为 true 时JavaScript 无法访问该 cookie防止 XSS 攻击默认为 true
// sameSite: 控制 cookie 是否可以跨站点发送常见值有 'Strict', 'Lax', 和 'None'
cookie: {
maxAge: 60000, // 60 秒
secure: false, // 如果使用 HTTPS请设置为 true
httpOnly: true, // 防止 JavaScript 访问 cookie
sameSite: 'lax' // 防止 CSRF 攻击
}
}));

app.get('/', (req, res) => {
if (req.session.views) {
req.session.views++;
res.setHeader('Content-Type', 'text/html');
res.write('<p>views: ' + req.session.views + '</p>');
res.end();
} else {
req.session.views = 1;
res.end('欢迎首次访问');
}
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器正在监听端口 ${PORT}`);
});
}).catch(console.error);

会话数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
app.get('/login', (req, res) => {
req.session.user = { id: 1, name: 'Alice' };
res.send('登录成功');
});

app.get('/profile', (req, res) => {
if (req.session.user) {
res.send(`你好, ${req.session.user.name}`);
} else {
res.send('请先登录');
}
});

app.get('/logout', (req, res) => {
delete req.session.user;
res.send('已注销');
});

销毁会话

1
2
3
4
5
6
7
8
app.get('/logout', (req, res) => {
req.session.destroy((err) => {
if (err) {
return res.send('注销失败');
}
res.send('已注销');
});
});

事件监听

1
2
3
4
5
6
7
app.use((req, res, next) => {
req.session.on('destroy', () => {
console.log('会话已销毁');
});

next();
});

JWT

在Express应用中JWTJSON Web Token通常用于用户认证和信息交换

安装

1
2
3
4
5
# jsonwebtoken 是一个用于生成和验证JWT的库它提供了创建签名的JWT以及验证解码JWT的功能
npm install jsonwebtoken

# express-jwt 是一个Express中间件它可以用来保护路由确保只有携带有效JWT的请求才能访问这些路由
npm install express-jwt

生成JWT

1
2
3
4
5
const jwt = require('jsonwebtoken');
const secretKey = 'your_secret_key'; // 自定义密钥

const token = jwt.sign({ userId: 123 }, secretKey, { expiresIn: '1h' });
console.log(token); // 返回一个加密的token字符串

验证JWT

1
2
3
4
5
6
7
jwt.verify(token, secretKey, (err, decoded) => {
if (err) {
console.log('Token无效:', err);
return;
}
console.log('Token解析:', decoded); // 解析出原始payload
});

路由拦截

1
2
3
4
5
6
7
8
9
10
11
const expressJwt = require('express-jwt');
const secretKey = 'your_secret_key';

// 这将对除了/login和/public以外的所有路径应用JWT验证
app.use(expressJwt({ secret: secretKey }).unless({ path: ['/login', '/public'] }));

// 受保护的路由
app.get('/profile', (req, res) => {
// req.user 包含了解析后的JWT payload
res.send(`欢迎, ${req.user.userId}`);
});

错误处理

1
2
3
4
5
6
7
// 当 express-jwt 中间件遇到无效或缺失的JWT时会抛出一个错误
// 需要通过添加全局错误处理器来管理这些错误
app.use((err, req, res, next) => {
if (err.name === 'UnauthorizedError') {
res.status(401).send('非法访问');
}
});

视图引擎

Express 支持多种模板引擎来渲染 HTML 页面常见的模板引擎包括 EJSPug 和 Handlebars

安装

1
npm install pug

使用

⭐⭐ 创建一个路由

1
2
3
4
5
6
app.set('view engine', 'pug');
app.set('views', './views'); // 设置视图文件目录

app.get('/', (req, res) => {
res.render('index', { title: 'Express', message: 'Hello there!' });
});

⭐⭐ 添加配置

1
2
3
4
5
6
# 然后在 views 目录下创建一个 index.pug 文件
html
head
title=title
body
h1=message

数据库

在 Node.js 中与 MySQL 数据库进行交互通常会使用 mysql 或 mysql2 模块这两个模块提供了相似的功能但 mysql2 在性能和功能上有一些改进

安装

1
npm install mysql

简单使用

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
const mysql = require('mysql');

// 创建连接
const connection = mysql.createConnection({
host: 'localhost', // 连接地址
user: 'root', // 登录用户名
password: 'password', // 登录密码
database: 'test_db' // 数据库名称
});

// 连接到数据库
connection.connect((err) => {
if (err) {
console.error('连接失败:', err.stack);
return;
}
console.log('成功连接到数据库');
});

// 执行查询
connection.query('SELECT 1 + 1 AS solution', (error, results, fields) => {
if (error) throw error;
console.log('结果:', results[0].solution);
});

// 关闭连接
connection.end();

连接池

为了提高性能和资源利用率建议使用连接池而不是直接创建单个连接连接池允许多个请求共享一组数据库连接从而减少频繁建立和关闭连接的开销

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const mysql = require('mysql');

// 创建连接池
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'password',
database: 'test_db',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
});

// 获取连接并执行查询
pool.query('SELECT 1 + 1 AS solution', (error, results, fields) => {
if (error) throw error;
console.log('结果:', results[0].solution);
});

// 关闭所有连接
pool.end();

事务处理

事务允许你将多个 SQL 语句作为一个原子操作来执行确保数据的一致性和完整性

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
42
43
const mysql = require('mysql');

const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'password',
database: 'test_db'
});

connection.connect();

// 开始事务
connection.beginTransaction((err) => {
if (err) { throw err; }

// 执行一系列 SQL 语句
connection.query('INSERT INTO accounts (name, balance) VALUES (?, ?)', ['Alice', 100], (error, results, fields) => {
if (error) {
return connection.rollback(() => {
throw error;
});
}

connection.query('UPDATE accounts SET balance = balance - ? WHERE name = ?', [50, 'Alice'], (error, results, fields) => {
if (error) {
return connection.rollback(() => {
throw error;
});
}

connection.commit((error) => {
if (error) {
return connection.rollback(() => {
throw error;
});
}
console.log('事务提交成功');
});
});
});
});

connection.end();

流式查询

对于大数据集可以使用流式查询来逐步读取数据避免一次性加载大量数据导致内存溢出

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
const mysql = require('mysql');

const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'password',
database: 'test_db'
});

connection.connect();

const query = connection.query('SELECT * FROM large_table');

query.on('error', (err) => {
console.error('查询错误:', err);
});

query.on('fields', (fields) => {
console.log('字段:', fields);
});

query.on('result', (row) => {
console.log('行:', row);
});

query.on('end', () => {
console.log('查询结束');
});

connection.end();

预处理语句

预处理语句可以防止 SQL 注入攻击并且在重复执行相同查询时提高性能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const mysql = require('mysql');

// 创建连接
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'password',
database: 'test_db'
});

connection.connect();

// 使用参数化查询
const userId = 1;
const userName = 'Alice';

const query = 'SELECT * FROM users WHERE id = ? AND name = ?';
connection.query(query, [userId, userName], (error, results, fields) => {
if (error) throw error;
console.log('查询结果:', results);
});

connection.end();

热更新

安装

1
npm install -g nodemon

使用

1
2
3
4
5
# 之前使用的是 node 命令启动项目
node index.js

# 热更新使用 nodemon 命令启动项目
nodemon index.js

学习资源