Node.js 教程 - 基于 Chrome V8 引擎的 JavaScript 运行时
一、项目概述
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时,它允许开发者使用 JavaScript 编写服务器端应用程序。Node.js 的出现彻底改变了 JavaScript 的应用范围,使它从一种仅限于浏览器的脚本语言转变为一种全栈开发语言。
1.1 核心概念
- V8 引擎:Google 开发的高性能 JavaScript 引擎,是 Node.js 的核心
- 事件循环:Node.js 的异步处理机制,用于处理非阻塞 I/O 操作
- 模块系统:基于 CommonJS 规范的模块系统,用于代码组织和复用
- NPM:Node.js 的包管理工具,用于管理依赖
- 异步 I/O:非阻塞的 I/O 操作,提高应用性能
- 事件驱动:基于事件的编程模型,适合处理并发请求
1.2 核心特点
- 异步非阻塞 I/O:提高应用性能和并发处理能力
- 事件驱动:基于事件的编程模型,适合处理并发请求
- 单线程:主线程是单线程的,避免了线程同步问题
- 跨平台:可以在 Windows、macOS、Linux 等平台上运行
- NPM 生态系统:丰富的第三方包,加速开发
- 高性能:基于 V8 引擎,执行 JavaScript 代码速度快
二、安装与设置
2.1 安装方式
Windows 安装:
- 访问 Node.js 官网
- 下载 Windows 安装包(LTS 版本推荐)
- 运行安装程序,按照提示完成安装
- 打开命令提示符,输入
node -v验证安装成功
macOS 安装:
- 访问 Node.js 官网
- 下载 macOS 安装包(LTS 版本推荐)
- 运行安装程序,按照提示完成安装
- 打开终端,输入
node -v验证安装成功
Linux 安装:
# 使用包管理器安装(Ubuntu/Debian)
sudo apt update
sudo apt install nodejs npm
# 或使用 nvm 安装(推荐)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.bashrc
nvm install node
nvm use node2.2 版本管理
使用 nvm 管理多个 Node.js 版本:
# 安装 nvm(Node Version Manager)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
# 安装特定版本的 Node.js
nvm install 18
nvm install 20
# 切换到特定版本
nvm use 18
# 设置默认版本
nvm alias default 18
# 查看已安装的版本
nvm list2.3 基本配置
npm 配置:
# 查看 npm 配置
npm config list
# 设置 npm 镜像(加速下载)
npm config set registry https://registry.npmmirror.com/
# 设置默认前缀(全局安装路径)
npm config set prefix "$HOME/.npm-global"创建项目:
# 创建项目目录
mkdir my-nodejs-project
cd my-nodejs-project
# 初始化项目
npm init
# 或使用默认值快速初始化
npm init -y三、基础用法
3.1 Hello World
创建简单的 Node.js 应用:
// index.js
console.log('Hello, Node.js!');运行应用:
node index.js3.2 模块系统
创建和使用模块:
// utils.js - 导出模块
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
module.exports = {
add,
subtract
};
// 或使用 ES6 模块语法(需要在 package.json 中设置 "type": "module")
// export { add, subtract };// index.js - 导入模块
const utils = require('./utils');
console.log(utils.add(2, 3)); // 输出: 5
console.log(utils.subtract(5, 2)); // 输出: 3
// 或使用 ES6 模块语法
// import { add, subtract } from './utils.js';
// console.log(add(2, 3));
// console.log(subtract(5, 2));3.3 文件系统操作
读取文件:
const fs = require('fs');
// 同步读取
try {
const data = fs.readFileSync('example.txt', 'utf8');
console.log(data);
} catch (err) {
console.error(err);
}
// 异步读取
fs.readFile('example.txt', 'utf8', (err, data) => {
if (err) {
console.error(err);
return;
}
console.log(data);
});写入文件:
const fs = require('fs');
// 同步写入
try {
fs.writeFileSync('output.txt', 'Hello, Node.js!');
console.log('文件写入成功');
} catch (err) {
console.error(err);
}
// 异步写入
fs.writeFile('output.txt', 'Hello, Node.js!', (err) => {
if (err) {
console.error(err);
return;
}
console.log('文件写入成功');
});3.4 创建 HTTP 服务器
简单的 HTTP 服务器:
const http = require('http');
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello, Node.js HTTP Server!\n');
});
const port = 3000;
server.listen(port, () => {
console.log(`服务器运行在 http://localhost:${port}/`);
});运行服务器:
node server.js然后在浏览器中访问 http://localhost:3000/ 查看结果。
四、高级特性
4.1 事件循环
理解事件循环:
const fs = require('fs');
console.log('1. 开始执行');
// 同步操作
fs.readFileSync('example.txt');
console.log('2. 同步读取文件完成');
// 异步操作
fs.readFile('example.txt', (err, data) => {
console.log('4. 异步读取文件完成');
});
console.log('3. 继续执行其他代码');
// 输出顺序: 1 -> 2 -> 3 -> 4事件循环的阶段:
- Timers:执行 setTimeout 和 setInterval 回调
- Pending Callbacks:执行延迟到下一个循环迭代的 I/O 回调
- Idle, Prepare:内部使用
- Poll:执行 I/O 回调,处理轮询队列
- Check:执行 setImmediate 回调
- Close Callbacks:执行 close 事件回调
4.2 异步编程模式
回调函数:
const fs = require('fs');
function readFileCallback(err, data) {
if (err) {
console.error(err);
return;
}
console.log(data);
}
fs.readFile('example.txt', 'utf8', readFileCallback);Promise:
const fs = require('fs').promises;
fs.readFile('example.txt', 'utf8')
.then(data => {
console.log(data);
})
.catch(err => {
console.error(err);
});async/await:
const fs = require('fs').promises;
async function readFileAsync() {
try {
const data = await fs.readFile('example.txt', 'utf8');
console.log(data);
} catch (err) {
console.error(err);
}
}
readFileAsync();4.3 Stream(流)
使用 Stream 读取大文件:
const fs = require('fs');
// 创建可读流
const readableStream = fs.createReadStream('large-file.txt', 'utf8');
// 监听数据事件
readableStream.on('data', (chunk) => {
console.log('接收到数据块:', chunk.length);
});
// 监听结束事件
readableStream.on('end', () => {
console.log('文件读取完成');
});
// 监听错误事件
readableStream.on('error', (err) => {
console.error('读取错误:', err);
});使用 Stream 复制文件:
const fs = require('fs');
// 创建可读流和可写流
const readableStream = fs.createReadStream('source.txt');
const writableStream = fs.createWriteStream('destination.txt');
// 管道操作
readableStream.pipe(writableStream);
// 监听完成事件
writableStream.on('finish', () => {
console.log('文件复制完成');
});4.4 子进程
创建子进程:
const { spawn } = require('child_process');
// 执行 ls 命令
const ls = spawn('ls', ['-la']);
// 监听 stdout 事件
ls.stdout.on('data', (data) => {
console.log(`标准输出: ${data}`);
});
// 监听 stderr 事件
ls.stderr.on('data', (data) => {
console.error(`标准错误: ${data}`);
});
// 监听退出事件
ls.on('close', (code) => {
console.log(`子进程退出码: ${code}`);
});执行 shell 命令:
const { exec } = require('child_process');
exec('ls -la', (err, stdout, stderr) => {
if (err) {
console.error(`执行错误: ${err}`);
return;
}
console.log(`标准输出: ${stdout}`);
if (stderr) {
console.error(`标准错误: ${stderr}`);
}
});五、实际应用场景
5.1 构建 RESTful API
使用 Express.js 构建 RESTful API:
// 首先安装 Express
// npm install express
const express = require('express');
const app = express();
const port = 3000;
// 中间件
app.use(express.json());
// 模拟数据
let users = [
{ id: 1, name: '张三', email: 'zhangsan@example.com' },
{ id: 2, name: '李四', email: 'lisi@example.com' }
];
// GET /users - 获取所有用户
app.get('/users', (req, res) => {
res.json(users);
});
// GET /users/:id - 获取单个用户
app.get('/users/:id', (req, res) => {
const id = parseInt(req.params.id);
const user = users.find(user => user.id === id);
if (!user) {
return res.status(404).json({ message: '用户不存在' });
}
res.json(user);
});
// POST /users - 创建用户
app.post('/users', (req, res) => {
const newUser = {
id: users.length + 1,
name: req.body.name,
email: req.body.email
};
users.push(newUser);
res.status(201).json(newUser);
});
// PUT /users/:id - 更新用户
app.put('/users/:id', (req, res) => {
const id = parseInt(req.params.id);
const userIndex = users.findIndex(user => user.id === id);
if (userIndex === -1) {
return res.status(404).json({ message: '用户不存在' });
}
users[userIndex] = {
...users[userIndex],
...req.body
};
res.json(users[userIndex]);
});
// DELETE /users/:id - 删除用户
app.delete('/users/:id', (req, res) => {
const id = parseInt(req.params.id);
const userIndex = users.findIndex(user => user.id === id);
if (userIndex === -1) {
return res.status(404).json({ message: '用户不存在' });
}
users.splice(userIndex, 1);
res.json({ message: '用户删除成功' });
});
app.listen(port, () => {
console.log(`服务器运行在 http://localhost:${port}/`);
});5.2 构建实时应用
使用 Socket.IO 构建实时聊天应用:
// 首先安装 Socket.IO
// npm install express socket.io
const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = socketIo(server);
const port = 3000;
// 提供静态文件
app.use(express.static('public'));
// 监听连接事件
io.on('connection', (socket) => {
console.log('新用户连接');
// 监听聊天消息
socket.on('chat message', (msg) => {
console.log('消息: ' + msg);
// 广播消息给所有用户
io.emit('chat message', msg);
});
// 监听断开连接事件
socket.on('disconnect', () => {
console.log('用户断开连接');
});
});
server.listen(port, () => {
console.log(`服务器运行在 http://localhost:${port}/`);
});创建客户端 HTML 文件:
<!-- public/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Socket.IO 聊天</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font: 13px Helvetica, Arial; }
form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
#messages { list-style-type: none; margin: 0; padding: 0; }
#messages li { padding: 5px 10px; }
#messages li:nth-child(odd) { background: #eee; }
</style>
</head>
<body>
<ul id="messages"></ul>
<form action="">
<input id="m" autocomplete="off" /><button>发送</button>
</form>
<script src="https://cdn.socket.io/4.0.1/socket.io.min.js"></script>
<script>
const socket = io();
document.querySelector('form').addEventListener('submit', (e) => {
e.preventDefault();
const message = document.getElementById('m').value;
socket.emit('chat message', message);
document.getElementById('m').value = '';
return false;
});
socket.on('chat message', (msg) => {
const li = document.createElement('li');
li.textContent = msg;
document.getElementById('messages').appendChild(li);
window.scrollTo(0, document.body.scrollHeight);
});
</script>
</body>
</html>5.3 构建命令行工具
创建简单的命令行工具:
// 首先安装 commander
// npm install commander
#!/usr/bin/env node
const commander = require('commander');
const fs = require('fs');
const path = require('path');
const program = new commander.Command();
program
.version('1.0.0')
.description('简单的文件操作命令行工具');
// 列出目录内容
program
.command('ls <directory>')
.description('列出目录内容')
.action((directory) => {
try {
const files = fs.readdirSync(directory);
console.log(`目录 ${directory} 中的文件:`);
files.forEach(file => {
const stats = fs.statSync(path.join(directory, file));
const type = stats.isDirectory() ? '目录' : '文件';
console.log(`${file} (${type})`);
});
} catch (err) {
console.error('错误:', err.message);
}
});
// 读取文件内容
program
.command('cat <file>')
.description('读取文件内容')
.action((file) => {
try {
const content = fs.readFileSync(file, 'utf8');
console.log(content);
} catch (err) {
console.error('错误:', err.message);
}
});
// 写入文件
program
.command('write <file> <content>')
.description('写入文件内容')
.action((file, content) => {
try {
fs.writeFileSync(file, content);
console.log(`成功写入文件 ${file}`);
} catch (err) {
console.error('错误:', err.message);
}
});
program.parse(process.argv);在 package.json 中配置:
{
"name": "my-cli-tool",
"version": "1.0.0",
"description": "简单的文件操作命令行工具",
"bin": {
"my-cli": "./cli.js"
},
"dependencies": {
"commander": "^8.0.0"
}
}安装并使用:
# 全局安装
npm install -g .
# 使用命令
my-cli ls .
my-cli cat example.txt
my-cli write output.txt "Hello, CLI!"六、性能优化建议
6.1 代码优化
- 使用异步 I/O:避免使用同步 I/O 操作,特别是在处理并发请求时
- 合理使用缓存:对于频繁访问的数据,使用缓存减少 I/O 操作
- 优化数据库查询:使用索引,避免全表扫描
- 使用 Stream 处理大文件:避免一次性加载大文件到内存
- 合理使用模块:按需加载模块,避免一次性加载所有模块
6.2 应用架构优化
- 使用集群模式:利用多核 CPU
- 负载均衡:在多服务器之间分配请求
- 微服务架构:将应用拆分为多个独立的服务
- CDN 加速:使用 CDN 分发静态资源
- 数据库优化:使用连接池,合理设计数据库结构
6.3 代码优化示例
使用异步 I/O:
// 不好的做法:使用同步 I/O
const fs = require('fs');
function handleRequest(req, res) {
const data = fs.readFileSync('large-file.txt'); // 阻塞主线程
res.end(data);
}
// 好的做法:使用异步 I/O
const fs = require('fs');
function handleRequest(req, res) {
fs.readFile('large-file.txt', (err, data) => { // 非阻塞
if (err) {
res.statusCode = 500;
res.end('服务器错误');
return;
}
res.end(data);
});
}使用集群模式:
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
console.log(`主进程 ${process.pid} 正在运行`);
// 衍生工作进程
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker) => {
console.log(`工作进程 ${worker.process.pid} 已退出`);
// 重启工作进程
cluster.fork();
});
} else {
// 工作进程创建 HTTP 服务器
http.createServer((req, res) => {
res.writeHead(200);
res.end('Hello World\n');
}).listen(8000);
console.log(`工作进程 ${process.pid} 已启动`);
}七、常见问题与解决方案
7.1 内存泄漏
问题:应用运行一段时间后内存使用量不断增加
解决方案:
- 使用
--expose-gc参数启动应用,手动触发垃圾回收 - 使用
node-heapdump生成堆快照,分析内存使用情况 - 避免全局变量积累
- 正确关闭数据库连接和文件句柄
- 使用
weakmap存储临时对象
7.2 回调地狱
问题:多层嵌套的回调函数,代码可读性差
解决方案:
- 使用 Promise
- 使用 async/await
- 使用 async.js 等库
示例:
// 回调地狱
fs.readFile('file1.txt', (err1, data1) => {
if (err1) throw err1;
fs.readFile('file2.txt', (err2, data2) => {
if (err2) throw err2;
fs.readFile('file3.txt', (err3, data3) => {
if (err3) throw err3;
console.log(data1 + data2 + data3);
});
});
});
// 使用 Promise
fs.promises.readFile('file1.txt')
.then(data1 => {
return Promise.all([data1, fs.promises.readFile('file2.txt')]);
})
.then(([data1, data2]) => {
return Promise.all([data1, data2, fs.promises.readFile('file3.txt')]);
})
.then(([data1, data2, data3]) => {
console.log(data1 + data2 + data3);
})
.catch(err => {
console.error(err);
});
// 使用 async/await
async function readFiles() {
try {
const data1 = await fs.promises.readFile('file1.txt');
const data2 = await fs.promises.readFile('file2.txt');
const data3 = await fs.promises.readFile('file3.txt');
console.log(data1 + data2 + data3);
} catch (err) {
console.error(err);
}
}
readFiles();7.3 模块版本冲突
问题:不同依赖包要求相同模块的不同版本
解决方案:
- 使用
npm ls查看依赖树 - 使用
npm dedupe减少重复依赖 - 在 package.json 中使用 resolutions 字段指定版本
- 考虑使用 yarn 或 pnpm 作为包管理器
7.4 端口被占用
问题:启动应用时提示端口已被占用
解决方案:
- 使用
lsof -i :端口号查看占用端口的进程 - 使用
kill 进程ID终止占用端口的进程 - 修改应用使用的端口
八、Node.js 与其他技术的比较
8.1 Node.js vs PHP
| 特性 | Node.js | PHP |
|---|---|---|
| 语言 | JavaScript | PHP |
| 执行模型 | 事件驱动,非阻塞 I/O | 同步,阻塞 I/O |
| 性能 | 高性能,适合并发请求 | 中等,并发处理能力较弱 |
| 生态系统 | NPM,丰富的包 | Composer,丰富的包 |
| 适用场景 | 实时应用,API 服务,微服务 | 传统 Web 应用,内容管理系统 |
| 学习曲线 | 中等,需要理解异步编程 | 低,语法简单 |
8.2 Node.js vs Python
| 特性 | Node.js | Python |
|---|---|---|
| 语言 | JavaScript | Python |
| 执行模型 | 事件驱动,非阻塞 I/O | 同步,阻塞 I/O(但有异步库) |
| 性能 | 高性能,适合并发请求 | 中等,计算密集型任务性能较好 |
| 生态系统 | NPM,前端友好 | Pip,数据科学和机器学习库丰富 |
| 适用场景 | 实时应用,API 服务,微服务 | 数据科学,机器学习,后端服务 |
| 学习曲线 | 中等,需要理解异步编程 | 低,语法简洁易读 |
8.3 Node.js vs Java
| 特性 | Node.js | Java |
|---|---|---|
| 语言 | JavaScript | Java |
| 执行模型 | 事件驱动,非阻塞 I/O | 多线程,阻塞 I/O |
| 性能 | 高性能,适合 I/O 密集型任务 | 高性能,适合计算密集型任务 |
| 生态系统 | NPM,轻量级 | Maven/Gradle,企业级库丰富 |
| 适用场景 | 实时应用,API 服务,微服务 | 企业级应用,大型系统 |
| 学习曲线 | 中等,需要理解异步编程 | 高,语法和生态系统复杂 |
九、参考资源
9.1 官方资源
9.2 学习资源
9.3 工具与框架
Web 框架:
- Express.js - 轻量级 Web 框架
- Koa.js - 由 Express 团队开发的下一代 Web 框架
- NestJS - 基于 TypeScript 的企业级框架
- Fastify - 高性能 Web 框架
数据库 ORM:
工具:
十、总结
Node.js 是一个强大的 JavaScript 运行时,它基于 Chrome V8 引擎,提供了异步非阻塞 I/O 和事件驱动的编程模型,使 JavaScript 能够在服务器端运行。Node.js 的出现彻底改变了 JavaScript 的应用范围,使它成为一种全栈开发语言。
Node.js 的核心优势在于:
- 异步非阻塞 I/O:提高应用性能和并发处理能力
- 事件驱动:基于事件的编程模型,适合处理并发请求
- 跨平台:可以在 Windows、macOS、Linux 等平台上运行
- NPM 生态系统:丰富的第三方包,加速开发
- 高性能:基于 V8 引擎,执行 JavaScript 代码速度快
Node.js 适合开发各种类型的应用,包括:
- API 服务:高性能的 RESTful API
- 实时应用:聊天应用、游戏服务器、协作工具
- 微服务:轻量级的服务架构
- 命令行工具:自动化脚本和工具
- 前端构建工具:webpack、gulp 等
通过本教程的学习,你应该已经掌握了 Node.js 的基本使用方法和高级特性,可以开始在项目中应用它来构建各种类型的应用了。随着实践经验的积累,你会发现 Node.js 不仅是一种技术,更是一种思维方式,它鼓励开发者采用异步、事件驱动的编程模式,从而构建出更加高效、可扩展的应用。
Node.js 的生态系统在不断发展壮大,新的框架和工具不断涌现,为开发者提供了更多的选择和便利。作为一名开发者,保持学习的态度,关注 Node.js 的最新发展,将会使你在技术道路上不断前进。