NodeJS
NodeJS 基础
NodeJS 作用、特点与应用场景
包含的题目:
- NodeJS 的作用是什么?
- NodeJS 的特点是什么?
- NodeJS 与浏览器的区别是什么?(高频考点)
- NodeJS 的应用场景有哪些?
详细答案:
NodeJS 作用:基于 Chrome V8 引擎的 JavaScript 运行时环境,用于构建高性能、可扩展的网络应用。
NodeJS 特点:
- 非阻塞 I/O:异步处理 I/O 操作,高并发处理能力
- 事件驱动:基于事件循环的编程模型
- 单线程:主线程单线程,通过事件循环处理并发
- 跨平台:支持 Windows、Linux、macOS
- NPM 生态:丰富的包管理器和模块库
- 高性能:V8 引擎编译执行,速度快
NodeJS vs 浏览器:
| 方面 | NodeJS | 浏览器 |
|---|---|---|
| 运行环境 | 服务器端 | 客户端 |
| 全局对象 | global | window |
| 文件系统 | 有完整访问权限 | 受限(沙盒) |
| DOM 操作 | 不支持 | 支持 |
| 模块系统 | CommonJS/ES Modules | ES Modules |
| I/O 操作 | 非阻塞,异步 | 有限制 |
| 应用场景 | 服务器、工具链 | 网页、Web 应用 |
应用场景:
- Web 服务器:Express、Koa、Fastify
- API 网关:微服务架构中的 API 聚合
- 实时应用:聊天、游戏、协作工具
- 工具开发:构建工具、CLI 工具
- 微服务:轻量级服务节点
- 中间层:BFF(Backend for Frontend)
- IoT 应用:物联网设备控制
NodeJS 模块系统
CommonJS 与 ES Modules
包含的题目: 5. 什么是 CommonJS?(高频考点) 6. require 函数的使用 7. module.exports 与 exports 的区别(高频考点) 8. 模块的加载机制是什么? 9. 模块的缓存机制是什么? 10. 如何解决模块的循环依赖? 11. CommonJS 与 ES Modules 的区别(高频考点)
详细答案:
CommonJS 模块:
// 导出模块
// math.js
module.exports = {
add: (a, b) => a + b,
subtract: (a, b) => a - b,
};
// 或
exports.add = (a, b) => a + b;
exports.subtract = (a, b) => a - b;
// 导入模块
// app.js
const math = require('./math.js');
console.log(math.add(1, 2));
module.exports vs exports:
module.exports:实际的导出对象exports:module.exports的引用- 重要:直接赋值给
exports无效,必须赋值给module.exports或exports的属性
模块加载机制:
- 路径分析:解析模块路径
- 文件定位:查找 .js、.json、.node 文件
- 编译执行:加载并执行模块代码
- 加入缓存:缓存模块,避免重复加载
缓存机制:每个模块第一次加载后会被缓存,后续 require 返回缓存结果。
循环依赖解决:
// a.js
console.log('a 开始');
exports.done = false;
const b = require('./b.js');
console.log('在 a 中,b.done = %j', b.done);
exports.done = true;
console.log('a 结束');
// b.js
console.log('b 开始');
exports.done = false;
const a = require('./a.js');
console.log('在 b 中,a.done = %j', a.done);
exports.done = true;
console.log('b 结束');
CommonJS vs ES Modules:
| 特性 | CommonJS | ES Modules |
|---|---|---|
| 语法 | require/module.exports | import/export |
| 加载 | 运行时加载,同步 | 编译时加载,异步 |
| 值类型 | 值的拷贝(基本类型) | 值的引用 |
| 动态性 | 支持动态 require | 静态 import |
| 树摇优化 | 不支持 | 支持 |
| 循环依赖 | 支持,但有风险 | 支持,更安全 |
ES Modules 示例:
// math.mjs
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
// app.mjs
import { add, subtract } from './math.mjs';
console.log(add(1, 2));
NodeJS 事件循环
事件循环原理与阶段
包含的题目: 12. 什么是事件循环?(高频考点) 13. 事件循环的原理是什么? 14. 事件循环的阶段有哪些?(高频考点) 15. timers 阶段的作用 16. poll 阶段的作用 17. check 阶段的作用 18. 什么是宏任务? 19. 什么是微任务? 20. setImmediate 的使用 21. process.nextTick 的使用 22. Node.js 与浏览器事件循环的区别(高频考点)
详细答案:
事件循环阶段:
- timers:执行 setTimeout 和 setInterval 回调
- pending callbacks:执行系统操作回调(如 TCP 错误)
- idle, prepare:内部使用
- poll:检索新的 I/O 事件,执行相关回调
- check:执行 setImmediate 回调
- close callbacks:执行关闭事件的回调(如 socket.on('close'))
事件循环流程:
┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ poll │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│ check │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│ close callbacks │
└───────────────────────────┘
宏任务 vs 微任务:
- 宏任务:setTimeout、setInterval、setImmediate、I/O 操作
- 微任务:process.nextTick、Promise.then/catch/finally、queueMicrotask
执行顺序:
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
process.nextTick(() => console.log('4'));
setImmediate(() => console.log('5'));
console.log('6');
// 输出顺序:1 6 4 3 2 5
// nextTick > Promise > setTimeout > setImmediate
setImmediate vs setTimeout:
setImmediate:在 check 阶段执行setTimeout:在 timers 阶段执行(最小延迟 1ms)
process.nextTick:
- 在当前操作完成后,事件循环继续之前执行
- 不属于事件循环的阶段
- 可能造成递归调用,导致事件循环饥饿
Node.js vs 浏览器事件循环:
| 环境 | 阶段 | 微任务队列 | 特点 |
|---|---|---|---|
| Node.js | 6个阶段 | nextTick 队列、Promise 队列 | 更复杂,支持更多 I/O |
| 浏览器 | 渲染前/后 | 单个微任务队列 | 与渲染相关,支持 requestAnimationFrame |
NodeJS 异步编程
回调、Promise、async/await
包含的题目: 23. 回调函数的使用 24. 如何解决回调地狱? 25. Promise 的使用 26. async/await 的使用 27. EventEmitter 的使用 28. Stream 的使用 29. Stream 的类型有哪些?
详细答案:
回调地狱:
// 回调地狱示例
fs.readFile('file1.txt', (err, data1) => {
if (err) throw err;
fs.readFile('file2.txt', (err, data2) => {
if (err) throw err;
fs.writeFile('result.txt', data1 + data2, (err) => {
if (err) throw err;
console.log('完成');
});
});
});
Promise 解决回调地狱:
const fs = require('fs').promises;
fs.readFile('file1.txt')
.then(data1 => fs.readFile('file2.txt'))
.then(data2 => fs.writeFile('result.txt', data1 + data2))
.then(() => console.log('完成'))
.catch(err => console.error(err));
async/await:
async function processFiles() {
try {
const data1 = await fs.readFile('file1.txt');
const data2 = await fs.readFile('file2.txt');
await fs.writeFile('result.txt', data1 + data2);
console.log('完成');
} catch (err) {
console.error(err);
}
}
EventEmitter 事件发射器:
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
// 监听事件
myEmitter.on('event', (arg1, arg2) => {
console.log('事件触发', arg1, arg2);
});
// 触发事件
myEmitter.emit('event', '参数1', '参数2');
// 一次性监听
myEmitter.once('once', () => {
console.log('只触发一次');
});
// 错误处理
myEmitter.on('error', (err) => {
console.error('错误:', err);
});
Stream 流处理:
const fs = require('fs');
// 可读流
const readable = fs.createReadStream('input.txt');
// 可写流
const writable = fs.createWriteStream('output.txt');
// 管道连接
readable.pipe(writable);
// 流事件
readable.on('data', (chunk) => {
console.log('收到数据:', chunk.length);
});
readable.on('end', () => {
console.log('读取完成');
});
writable.on('finish', () => {
console.log('写入完成');
});
Stream 类型:
- Readable:可读流(文件读取、HTTP 请求)
- Writable:可写流(文件写入、HTTP 响应)
- Duplex:双向流(TCP Socket)
- Transform:转换流(压缩、加密)
NodeJS 文件系统
fs 模块操作
包含的题目: 30. NodeJS 的文件系统模块 31. fs 模块的使用 32. 文件的读取方法 33. 文件的写入方法 34. 文件的删除方法 35. 文件的监听方法 36. 同步与异步文件操作的区别
详细答案:
文件读取:
const fs = require('fs');
// 异步读取
fs.readFile('file.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log(data);
});
// Promise 方式(Node.js 14+)
const fs = require('fs').promises;
fs.readFile('file.txt', 'utf8')
.then(data => console.log(data))
.catch(err => console.error(err));
// 同步读取(阻塞)
try {
const data = fs.readFileSync('file.txt', 'utf8');
console.log(data);
} catch (err) {
console.error(err);
}
文件写入:
// 异步写入
fs.writeFile('file.txt', 'Hello Node.js', (err) => {
if (err) throw err;
console.log('写入完成');
});
// 追加写入
fs.appendFile('file.txt', '\n追加内容', (err) => {
if (err) throw err;
console.log('追加完成');
});
// 流式写入
const writeStream = fs.createWriteStream('file.txt');
writeStream.write('第一行\n');
writeStream.write('第二行\n');
writeStream.end();
文件操作:
// 检查文件是否存在
fs.access('file.txt', fs.constants.F_OK, (err) => {
console.log(err ? '不存在' : '存在');
});
// 删除文件
fs.unlink('file.txt', (err) => {
if (err) throw err;
console.log('删除完成');
});
// 重命名/移动文件
fs.rename('old.txt', 'new.txt', (err) => {
if (err) throw err;
console.log('重命名完成');
});
// 复制文件
fs.copyFile('source.txt', 'dest.txt', (err) => {
if (err) throw err;
console.log('复制完成');
});
文件监听:
// 监听文件变化
fs.watch('file.txt', (eventType, filename) => {
console.log(`事件类型: ${eventType}`);
if (filename) {
console.log(`文件名: ${filename}`);
}
});
// 更稳定的监听(推荐)
const chokidar = require('chokidar');
const watcher = chokidar.watch('file.txt', {
persistent: true,
ignoreInitial: false,
});
watcher.on('change', (path) => {
console.log(`文件变化: ${path}`);
});
目录操作:
// 创建目录
fs.mkdir('newdir', { recursive: true }, (err) => {
if (err) throw err;
console.log('目录创建完成');
});
// 读取目录
fs.readdir('.', (err, files) => {
if (err) throw err;
console.log('目录内容:', files);
});
// 删除目录
fs.rmdir('dir', { recursive: true }, (err) => {
if (err) throw err;
console.log('目录删除完成');
});
NodeJS 网络编程
HTTP、TCP、WebSocket 服务器
包含的题目: 37. HTTP 模块的使用 38. 如何创建 HTTP 服务器? 39. HTTP 请求的处理 40. HTTP 响应的处理 41. TCP 模块的使用 42. 如何创建 TCP 服务器? 43. WebSocket 的使用
详细答案:
HTTP 服务器:
const http = require('http');
const server = http.createServer((req, res) => {
// 请求信息
console.log('方法:', req.method);
console.log('URL:', req.url);
console.log('头部:', req.headers);
// 设置响应头
res.setHeader('Content-Type', 'application/json');
res.setHeader('X-Powered-By', 'Node.js');
// 根据不同路由处理
if (req.url === '/') {
res.statusCode = 200;
res.end(JSON.stringify({ message: '首页' }));
} else if (req.url === '/api/data') {
res.statusCode = 200;
res.end(JSON.stringify({ data: 'API 数据' }));
} else {
res.statusCode = 404;
res.end(JSON.stringify({ error: '未找到' }));
}
});
// 监听端口
server.listen(3000, () => {
console.log('服务器运行在 http://localhost:3000');
});
// 请求体处理
let body = [];
req.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString();
console.log('请求体:', body);
});
HTTPS 服务器:
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem'),
};
https.createServer(options, (req, res) => {
res.writeHead(200);
res.end('HTTPS 服务器');
}).listen(443);
TCP 服务器:
const net = require('net');
const server = net.createServer((socket) => {
console.log('客户端连接:', socket.remoteAddress);
// 接收数据
socket.on('data', (data) => {
console.log('收到数据:', data.toString());
socket.write('服务器回应: ' + data);
});
// 连接关闭
socket.on('end', () => {
console.log('客户端断开');
});
// 错误处理
socket.on('error', (err) => {
console.error('Socket 错误:', err);
});
});
// 监听端口
server.listen(8080, () => {
console.log('TCP 服务器运行在端口 8080');
});
WebSocket 服务器:
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => {
console.log('新客户端连接');
// 接收消息
ws.on('message', (message) => {
console.log('收到消息:', message);
// 广播给所有客户端
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(`广播: ${message}`);
}
});
});
// 发送欢迎消息
ws.send('欢迎连接 WebSocket 服务器');
// 连接关闭
ws.on('close', () => {
console.log('客户端断开');
});
});
NodeJS 进程与线程
进程管理、Worker Threads
包含的题目: 44. NodeJS 的进程 45. process 对象的使用 46. 环境变量的获取 47. 命令行参数的获取 48. Worker Threads 的使用 49. child_process 模块的使用 50. exec、spawn、fork 方法的区别(高频考点) 51. 进程间通信的方法
详细答案:
process 对象:
// 环境变量
console.log('NODE_ENV:', process.env.NODE_ENV);
console.log('PATH:', process.env.PATH);
// 命令行参数
console.log('参数:', process.argv);
// node app.js arg1 arg2
// process.argv[0]: node 路径
// process.argv[1]: 脚本路径
// process.argv[2]: 'arg1'
// 进程信息
console.log('进程ID:', process.pid);
console.log('工作目录:', process.cwd());
console.log('平台:', process.platform);
console.log('内存使用:', process.memoryUsage());
// 退出进程
process.exit(0); // 正常退出
process.exit(1); // 错误退出
// 信号处理
process.on('SIGINT', () => {
console.log('收到 SIGINT 信号');
process.exit(0);
});
子进程创建方法对比:
| 方法 | 特点 | 适用场景 |
|---|---|---|
| exec | 创建 shell,缓存输出 | 简单命令,输出较小 |
| spawn | 流式输入输出,无 shell | 大输出,实时处理 |
| fork | 创建 Node.js 子进程,IPC | 计算密集型任务 |
| execFile | 执行可执行文件 | 执行外部程序 |
exec 示例:
const { exec } = require('child_process');
exec('ls -la', (error, stdout, stderr) => {
if (error) {
console.error(`执行错误: ${error}`);
return;
}
console.log(`输出: ${stdout}`);
if (stderr) console.error(`错误: ${stderr}`);
});
spawn 示例:
const { spawn } = require('child_process');
const ls = spawn('ls', ['-la']);
ls.stdout.on('data', (data) => {
console.log(`输出: ${data}`);
});
ls.stderr.on('data', (data) => {
console.error(`错误: ${data}`);
});
ls.on('close', (code) => {
console.log(`进程退出,代码: ${code}`);
});
fork 示例:
// parent.js
const { fork } = require('child_process');
const child = fork('./child.js');
child.send({ message: 'Hello child' });
child.on('message', (msg) => {
console.log('来自子进程:', msg);
});
child.on('exit', (code) => {
console.log('子进程退出:', code);
});
// child.js
process.on('message', (msg) => {
console.log('来自父进程:', msg);
process.send({ message: 'Hello parent' });
});
Worker Threads:
const { Worker, isMainThread, parentPort } = require('worker_threads');
if (isMainThread) {
// 主线程
const worker = new Worker(__filename);
worker.on('message', (msg) => {
console.log('来自 Worker:', msg);
});
worker.postMessage('Hello Worker');
} else {
// Worker 线程
parentPort.on('message', (msg) => {
console.log('来自主线程:', msg);
parentPort.postMessage('Hello Main');
// 执行 CPU 密集型任务
const result = heavyCalculation();
parentPort.postMessage({ result });
});
}
进程间通信(IPC):
- 管道(pipe):父子进程通信
- 消息队列:系统级消息传递
- 共享内存:多个进程访问同一内存区域
- Socket:网络套接字通信
- 文件:通过文件系统通信
NodeJS 缓冲区
Buffer 操作
包含的题目: 52. 什么是 Buffer? 53. 如何创建 Buffer? 54. Buffer 的操作方法
详细答案:
Buffer 作用:处理二进制数据,如图片、文件、网络流等。
创建 Buffer:
// 方式1:指定大小
const buf1 = Buffer.alloc(10); // 10字节,填充 0
const buf2 = Buffer.allocUnsafe(10); // 10字节,不初始化
// 方式2:从数组
const buf3 = Buffer.from([1, 2, 3, 4]);
// 方式3:从字符串
const buf4 = Buffer.from('Hello', 'utf8');
// 方式4:从已有 Buffer
const buf5 = Buffer.from(buf4);
Buffer 操作:
const buf = Buffer.from('Hello World');
// 长度
console.log('长度:', buf.length);
// 读取
console.log('第1字节:', buf[0]); // 72 (H)
console.log('字符串:', buf.toString('utf8'));
// 写入
buf.write('Node', 0, 4, 'utf8');
console.log('修改后:', buf.toString());
// 复制
const buf2 = Buffer.alloc(11);
buf.copy(buf2);
console.log('复制:', buf2.toString());
// 比较
const buf3 = Buffer.from('Hello');
console.log('比较:', buf.compare(buf3));
// 切片
const slice = buf.slice(0, 5);
console.log('切片:', slice.toString());
// 连接
const buf4 = Buffer.from(' ');
const buf5 = Buffer.from('World');
const concat = Buffer.concat([buf, buf4, buf5]);
console.log('连接:', concat.toString());
// 填充
const buf6 = Buffer.alloc(10);
buf6.fill('A');
console.log('填充:', buf6.toString());
// 查找
console.log('查找 o:', buf.indexOf('o'));
console.log('查找 World:', buf.indexOf('World'));
// 遍历
for (const byte of buf) {
console.log('字节:', byte);
}
// 转 JSON
const json = buf.toJSON();
console.log('JSON:', json);
NodeJS 包管理
npm、yarn、pnpm
包含的题目: 55. npm 的使用 56. 常用的 npm 命令 57. package.json 的配置 58. dependencies 与 devDependencies 的区别(高频考点) 59. npm、yarn、pnpm 的区别(高频考点)
详细答案:
package.json 示例:
{
"name": "my-app",
"version": "1.0.0",
"description": "Node.js 应用",
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js",
"test": "jest",
"build": "webpack",
"lint": "eslint ."
},
"dependencies": {
"express": "^4.18.0",
"lodash": "^4.17.21"
},
"devDependencies": {
"nodemon": "^2.0.20",
"jest": "^29.0.0",
"eslint": "^8.0.0"
},
"peerDependencies": {
"react": ">=16.8.0"
},
"engines": {
"node": ">=14.0.0",
"npm": ">=6.0.0"
},
"keywords": ["node", "express", "api"],
"author": "Your Name",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/username/repo.git"
}
}
依赖类型:
- dependencies:生产环境依赖
- devDependencies:开发环境依赖
- peerDependencies:对等依赖,插件需要的主包版本
- optionalDependencies:可选依赖,安装失败不阻塞
- bundledDependencies:打包依赖,发布时包含
常用 npm 命令:
# 初始化项目
npm init
npm init -y
# 安装依赖
npm install express
npm install express --save
npm install eslint --save-dev
npm install # 安装所有依赖
# 全局安装
npm install -g nodemon
# 更新依赖
npm update
npm update express
# 卸载依赖
npm uninstall express
npm uninstall eslint --save-dev
# 查看信息
npm list
npm list --depth=0
npm view express version
npm outdated
# 运行脚本
npm run start
npm run dev
npm test
# 发布包
npm login
npm publish
npm version patch/minor/major
包管理工具对比:
| 工具 | 特点 | 优势 |
|---|---|---|
| npm | Node.js 官方包管理器 | 生态最全,兼容性好 |
| yarn | Facebook 开发 | 速度快,确定性安装 |
| pnpm | 硬链接+符号链接 | 节省磁盘空间,安装快 |
yarn 命令:
yarn add express
yarn add eslint --dev
yarn remove express
yarn upgrade
yarn why express # 查看依赖原因
pnpm 命令:
pnpm add express
pnpm add eslint -D
pnpm remove express
pnpm update
NodeJS 性能优化
性能优化方法与工具
包含的题目: 60. NodeJS 性能优化的方法有哪些? 61. 如何检测内存泄漏? 62. 如何解决内存泄漏? 63. 集群模式的使用 64. 负载均衡的实现
详细答案:
性能优化方法:
- 代码优化:避免同步阻塞操作,使用异步
- 内存管理:及时释放不再使用的对象
- 缓存策略:使用 Redis/Memcached 缓存数据
- 连接池:数据库连接复用
- 集群模式:多进程利用多核 CPU
- 负载均衡:分发请求到多个实例
- 监控告警:实时监控性能指标
内存泄漏检测:
// 使用 --inspect 参数
node --inspect app.js
// 使用内存分析工具
const { performance, memoryUsage } = require('perf_hooks');
setInterval(() => {
const mem = memoryUsage();
console.log(`内存使用:
RSS: ${Math.round(mem.rss / 1024 / 1024)}MB
HeapTotal: ${Math.round(mem.heapTotal / 1024 / 1024)}MB
HeapUsed: ${Math.round(mem.heapUsed / 1024 / 1024)}MB
External: ${Math.round(mem.external / 1024 / 1024)}MB`);
}, 5000);
// 使用 heapdump
const heapdump = require('heapdump');
setInterval(() => {
const filename = `heapdump-${Date.now()}.heapsnapshot`;
heapdump.writeSnapshot(filename, (err) => {
if (err) console.error(err);
else console.log(`堆快照已保存: ${filename}`);
});
}, 60000);
常见内存泄漏原因:
- 全局变量:意外创建全局变量
- 闭包:不当使用闭包保持引用
- 定时器:未清除的 setInterval/setTimeout
- 事件监听:未移除的事件监听器
- 缓存增长:无限增长的缓存
集群模式:
const cluster = require('cluster');
const os = require('os');
if (cluster.isMaster) {
const numCPUs = os.cpus().length;
console.log(`主进程 ${process.pid} 启动`);
// 衍生工作进程
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`工作进程 ${worker.process.pid} 退出`);
cluster.fork(); // 重启工作进程
});
} else {
// 工作进程共享同一个端口
const app = require('./app');
app.listen(3000);
console.log(`工作进程 ${process.pid} 启动`);
}
负载均衡策略:
- 轮询(Round Robin):依次分配请求
- 最少连接(Least Connections):分配给连接数最少的服务器
- IP 哈希(IP Hash):根据客户端IP分配
- 加权轮询(Weighted Round Robin):根据服务器权重分配
使用 PM2 进程管理:
# 安装
npm install -g pm2
# 启动应用
pm2 start app.js
pm2 start app.js -i max # 集群模式
# 管理进程
pm2 list
pm2 monit
pm2 logs
pm2 restart app
pm2 stop app
pm2 delete app
# 配置
pm2 ecosystem
NodeJS 安全
安全防护措施
包含的题目: 65. NodeJS 的安全问题有哪些? 66. 如何防范 SQL 注入? 67. 如何防范 XSS 攻击? 68. 如何防范 CSRF 攻击? 69. Helmet 的使用 70. CORS 的配置
详细答案:
安全防护:
输入验证:
const validator = require('validator');
// 验证输入
const email = req.body.email;
if (!validator.isEmail(email)) {
return res.status(400).json({ error: '邮箱格式错误' });
}
const sanitized = validator.escape(req.body.content); // 转义 HTML
SQL 注入防护:
// 使用参数化查询
const sql = 'SELECT * FROM users WHERE username = ? AND password = ?';
db.query(sql, [username, password], (err, results) => {
// 处理结果
});
// 使用 ORM(如 Sequelize)
const user = await User.findOne({
where: { username, password }
});
XSS 防护:
// 设置安全头部
app.use(helmet());
// 转义用户输入
const escapeHtml = (text) => {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
return text.replace(/[&<>"']/g, m => map[m]);
};
CSRF 防护:
const csrf = require('csurf');
const cookieParser = require('cookie-parser');
app.use(cookieParser());
app.use(csrf({ cookie: true }));
// 提供 CSRF token
app.get('/csrf-token', (req, res) => {
res.json({ csrfToken: req.csrfToken() });
});
// 验证请求
app.post('/api/data', (req, res) => {
// csurf 中间件已自动验证
res.json({ success: true });
});
Helmet 安全头部:
const helmet = require('helmet');
app.use(helmet());
// 自定义配置
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", "data:", "https:"],
},
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true,
},
}));
CORS 配置:
const cors = require('cors');
// 基本配置
app.use(cors());
// 自定义配置
app.use(cors({
origin: ['https://example.com', 'https://www.example.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 86400,
}));
// 动态配置
app.use((req, res, next) => {
const allowedOrigins = ['https://example.com'];
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.setHeader('Access-Control-Allow-Credentials', 'true');
if (req.method === 'OPTIONS') {
return res.sendStatus(200);
}
next();
});
NodeJS 常见问题
问题诊断与解决
包含的题目: 71. 如何解决内存泄漏问题? 72. 如何解决事件循环阻塞问题? 73. 如何处理异步错误? 74. 如何解决端口占用问题? 75. 如何解决跨域问题?
详细答案:
内存泄漏解决:
- 使用分析工具:Chrome DevTools、heapdump、clinic.js
- 代码审查:检查全局变量、闭包、定时器、事件监听器
- 压力测试:模拟高并发场景,观察内存变化
- 监控告警:设置内存阈值,自动报警
事件循环阻塞解决:
// 避免 CPU 密集型任务阻塞
// ❌ 错误:同步计算密集型任务
app.get('/compute', (req, res) => {
let result = 0;
for (let i = 0; i < 1e9; i++) {
result += i;
}
res.json({ result });
});
// ✅ 正确:使用 Worker Threads
app.get('/compute', async (req, res) => {
const worker = new Worker('./compute-worker.js');
worker.postMessage({ type: 'compute' });
worker.on('message', (result) => {
res.json({ result });
worker.terminate();
});
});
异步错误处理:
// Promise 错误处理
asyncFunction()
.then(result => {
// 成功处理
})
.catch(error => {
console.error('Promise 错误:', error);
// 错误处理
});
// async/await 错误处理
async function handleRequest() {
try {
const result = await asyncFunction();
// 成功处理
} catch (error) {
console.error('async/await 错误:', error);
// 错误处理
}
}
// 全局错误处理
process.on('unhandledRejection', (reason, promise) => {
console.error('未处理的 Promise 拒绝:', reason);
// 记录日志或重启应用
});
process.on('uncaughtException', (error) => {
console.error('未捕获的异常:', error);
// 记录日志,优雅关闭
process.exit(1);
});
端口占用解决:
const server = app.listen(3000, () => {
console.log('服务器启动');
});
// 优雅关闭
process.on('SIGTERM', () => {
console.log('收到 SIGTERM 信号,优雅关闭');
server.close(() => {
console.log('服务器关闭');
process.exit(0);
});
// 强制关闭超时
setTimeout(() => {
console.log('强制关闭');
process.exit(1);
}, 10000);
});
// 端口被占用时自动重试
function startServer(port) {
const server = app.listen(port, () => {
console.log(`服务器运行在端口 ${port}`);
}).on('error', (err) => {
if (err.code === 'EADDRINUSE') {
console.log(`端口 ${port} 被占用,尝试 ${port + 1}`);
startServer(port + 1);
} else {
console.error('服务器错误:', err);
}
});
}
startServer(3000);
性能问题诊断:
-
使用性能分析工具:
node --prof app.js node --prof-process isolate-0xnnnnnnn-v8.log > processed.txt -
监控关键指标:
- CPU 使用率
- 内存使用情况
- 请求响应时间
- 错误率
-
日志记录:
const morgan = require('morgan'); app.use(morgan('combined')); // 访问日志
以上是对 NodeJS 75 道面试题目的综合解答,涵盖了 NodeJS 基础、模块系统、事件循环、异步编程、文件系统、网络编程、进程管理、性能优化和安全等核心知识点。