一、Node.js基础概念
1.1 什么是Node.js?
Node.js是一个基于Chrome V8引擎的JavaScript运行环境,让JavaScript可以在服务器端运行。它使用事件驱动、非阻塞I/O模型,使其轻量且高效。
1.2 Node.js的历史
- 2009年:Ryan Dahl创建了Node.js
- 2010年:NPM(Node Package Manager)诞生
- 2011年:npm 1.0发布
- 2015年:Node.js基金会成立
- 2016年:引入长期支持(LTS)版本
- 至今:持续快速发展,广泛应用于后端开发
1.3 Node.js的特点
- 单线程:使用单线程事件循环,避免线程切换开销
- 异步非阻塞I/O:提高并发处理能力
- 跨平台:可在Windows、Linux、Mac上运行
- NPM生态:拥有世界上最庞大的开源库生态系统
- JavaScript全栈:前后端统一语言,降低开发成本
1.4 Node.js的应用场景
- Web应用:RESTful API、GraphQL服务
- 实时应用:聊天应用、实时协作工具
- 微服务:构建分布式系统
- CLI工具:命令行工具开发
- 数据流:处理大量数据
- 物联网:IoT设备数据处理
二、Node.js安装和环境配置
2.1 安装Node.js
Windows系统
- 访问Node.js官网:https://nodejs.org/
- 下载LTS版本(推荐)或Current版本
- 运行安装程序,按提示完成安装
- 验证安装:
bash
node --version
npm --version
Linux系统
bash
# 使用包管理器安装
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install nodejs npm
# CentOS/RHEL
sudo yum install nodejs npm
# 使用NVM安装(推荐)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
source ~/.bashrc
nvm install --lts
nvm use --lts
Mac系统
bash
# 使用Homebrew安装
brew install node
# 使用NVM安装
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
source ~/.bashrc
nvm install --lts
2.2 版本管理
使用NVM(Node Version Manager)
bash
# 安装NVM
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
# 查看可用版本
nvm list-remote
# 安装特定版本
nvm install 16.14.0
# 使用特定版本
nvm use 16.14.0
# 设置默认版本
nvm alias default 16.14.0
# 查看已安装版本
nvm list
# 卸载版本
nvm uninstall 16.14.0
使用n(简单版本管理器)
bash
# 安装n
npm install -g n
# 安装最新LTS版本
n lts
# 安装最新版本
n latest
# 安装特定版本
n 16.14.0
# 切换版本
n
# 查看已安装版本
n ls
2.3 环境变量配置
bash
# Windows系统
# 系统环境变量中添加:
# NODE_PATH = C:\Users\YourName\AppData\Roaming\npm\node_modules
# PATH = %NODE_PATH%;C:\Program Files\nodejs
# Linux/Mac系统
# 在 ~/.bashrc 或 ~/.zshrc 中添加:
export NODE_PATH=/usr/local/lib/node_modules
export PATH=$PATH:/usr/local/bin
# 重新加载配置
source ~/.bashrc
2.4 创建第一个Node.js应用
javascript
// hello.js
console.log('Hello, Node.js!');
// 运行
node hello.js
javascript
// http-server.js
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello, Node.js!');
});
server.listen(3000, () => {
console.log('Server running at http://localhost:3000/');
});
// 运行
node http-server.js
三、模块系统
3.1 CommonJS模块系统
导出模块
javascript
// math.js
// 方式1:导出单个值
module.exports = {
add: function(a, b) {
return a + b;
},
subtract: function(a, b) {
return a - b;
}
};
// 方式2:导出多个值
exports.add = function(a, b) {
return a + b;
};
exports.subtract = function(a, b) {
return a - b;
};
// 方式3:导出类
class Calculator {
add(a, b) {
return a + b;
}
}
module.exports = Calculator;
导入模块
javascript
// app.js
// 方式1:导入整个模块
const math = require('./math');
console.log(math.add(1, 2)); // 3
// 方式2:解构导入
const { add, subtract } = require('./math');
console.log(add(1, 2)); // 3
// 方式3:导入类
const Calculator = require('./math');
const calc = new Calculator();
console.log(calc.add(1, 2)); // 3
3.2 ES6模块系统
启用ES6模块
json
// package.json
{
"type": "module"
}
导出模块
javascript
// math.mjs
// 方式1:命名导出
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
// 方式2:默认导出
export default class Calculator {
add(a, b) {
return a + b;
}
}
// 方式3:混合导出
export const PI = 3.14159;
export function multiply(a, b) {
return a * b;
}
导入模块
javascript
// app.mjs
// 方式1:导入命名导出
import { add, subtract } from './math.mjs';
console.log(add(1, 2)); // 3
// 方式2:导入默认导出
import Calculator from './math.mjs';
const calc = new Calculator();
console.log(calc.add(1, 2)); // 3
// 方式3:导入所有
import * as math from './math.mjs';
console.log(math.add(1, 2)); // 3
// 方式4:动态导入
const module = await import('./math.mjs');
console.log(module.add(1, 2)); // 3
3.3 核心模块
1. fs(文件系统)
javascript
const fs = require('fs');
const path = require('path');
// 同步读取文件
try {
const data = fs.readFileSync('input.txt', 'utf8');
console.log(data);
} catch (error) {
console.error('读取文件失败:', error);
}
// 异步读取文件(推荐)
fs.readFile('input.txt', 'utf8', (error, data) => {
if (error) {
console.error('读取文件失败:', error);
return;
}
console.log(data);
});
// 使用Promise
const fsPromises = require('fs').promises;
async function readFileAsync() {
try {
const data = await fsPromises.readFile('input.txt', 'utf8');
console.log(data);
} catch (error) {
console.error('读取文件失败:', error);
}
}
// 写入文件
fs.writeFile('output.txt', 'Hello, Node.js!', 'utf8', (error) => {
if (error) {
console.error('写入文件失败:', error);
return;
}
console.log('文件写入成功');
});
// 追加内容
fs.appendFile('output.txt', '\n追加的内容', 'utf8', (error) => {
if (error) {
console.error('追加内容失败:', error);
return;
}
console.log('内容追加成功');
});
// 删除文件
fs.unlink('output.txt', (error) => {
if (error) {
console.error('删除文件失败:', error);
return;
}
console.log('文件删除成功');
});
// 创建目录
fs.mkdir('newdir', { recursive: true }, (error) => {
if (error) {
console.error('创建目录失败:', error);
return;
}
console.log('目录创建成功');
});
// 读取目录
fs.readdir('.', (error, files) => {
if (error) {
console.error('读取目录失败:', error);
return;
}
console.log('目录内容:', files);
});
// 检查文件是否存在
fs.access('input.txt', fs.constants.F_OK, (error) => {
if (error) {
console.log('文件不存在');
} else {
console.log('文件存在');
}
});
// 获取文件信息
fs.stat('input.txt', (error, stats) => {
if (error) {
console.error('获取文件信息失败:', error);
return;
}
console.log('文件信息:', stats);
console.log('是否为文件:', stats.isFile());
console.log('是否为目录:', stats.isDirectory());
console.log('文件大小:', stats.size);
});
// 路径操作
const filePath = path.join(__dirname, 'files', 'input.txt');
console.log('文件路径:', filePath);
console.log('文件名:', path.basename(filePath));
console.log('目录名:', path.dirname(filePath));
console.log('扩展名:', path.extname(filePath));
console.log('绝对路径:', path.resolve(filePath));
2. http(HTTP服务器)
javascript
const http = require('http');
const url = require('url');
const querystring = require('querystring');
// 创建简单的HTTP服务器
const server = http.createServer((req, res) => {
// 处理CORS
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
// 处理OPTIONS请求
if (req.method === 'OPTIONS') {
res.writeHead(200);
res.end();
return;
}
// 解析URL
const parsedUrl = url.parse(req.url, true);
const pathname = parsedUrl.pathname;
const query = parsedUrl.query;
console.log(`收到请求: ${req.method} ${pathname}`);
// 路由处理
if (pathname === '/') {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end(`
<!DOCTYPE html>
<html>
<head>
<title>Node.js HTTP服务器</title>
</head>
<body>
<h1>欢迎使用Node.js HTTP服务器</h1>
<p>当前路径: ${pathname}</p>
</body>
</html>
`);
} else if (pathname === '/api/data') {
// JSON响应
const data = {
message: '成功',
data: [1, 2, 3, 4, 5]
};
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
res.end(JSON.stringify(data));
} else {
// 404页面
res.writeHead(404, { 'Content-Type': 'text/html; charset=utf-8' });
res.end(`
<!DOCTYPE html>
<html>
<head>
<title>404 Not Found</title>
</head>
<body>
<h1>404 - 页面不存在</h1>
<p>您访问的页面不存在: ${pathname}</p>
</body>
</html>
`);
}
});
server.listen(3000, () => {
console.log('服务器运行在 http://localhost:3000/');
});
// 处理POST请求
const server2 = http.createServer((req, res) => {
if (req.method === 'POST' && req.url === '/api/login') {
let body = '';
req.on('data', chunk => {
body += chunk.toString();
});
req.on('end', () => {
try {
const data = JSON.parse(body);
console.log('接收到的数据:', data);
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
res.end(JSON.stringify({
success: true,
message: '登录成功',
user: data
}));
} catch (error) {
res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
res.end(JSON.stringify({
success: false,
message: '数据格式错误'
}));
}
});
} else {
res.writeHead(404, { 'Content-Type': 'text/html; charset=utf-8' });
res.end('404 Not Found');
}
});
server2.listen(3001, () => {
console.log('POST服务器运行在 http://localhost:3001/');
});
3. path(路径处理)
javascript
const path = require('path');
// 路径拼接
const fullPath = path.join(__dirname, 'files', 'data.txt');
console.log('完整路径:', fullPath);
// 路径解析
const parsed = path.parse('/path/to/file.txt');
console.log('路径解析:', parsed);
// {
// root: '/',
// dir: '/path/to',
// base: 'file.txt',
// ext: '.txt',
// name: 'file'
// }
// 获取文件名
console.log('文件名:', path.basename('/path/to/file.txt')); // file.txt
console.log('不含扩展名的文件名:', path.basename('/path/to/file.txt', '.txt')); // file
// 获取目录名
console.log('目录名:', path.dirname('/path/to/file.txt')); // /path/to
// 获取扩展名
console.log('扩展名:', path.extname('/path/to/file.txt')); // .txt
// 获取绝对路径
console.log('绝对路径:', path.resolve('files/data.txt'));
// 规范化路径
console.log('规范化路径:', path.normalize('/path/to/../to/./file.txt')); // /path/to/file.txt
// 判断是否为绝对路径
console.log('是否为绝对路径:', path.isAbsolute('/path/to/file.txt')); // true
console.log('是否为绝对路径:', path.isAbsolute('files/data.txt')); // false
// 获取当前工作目录
console.log('当前工作目录:', process.cwd());
console.log('__dirname:', __dirname);
console.log('__filename:', __filename);
4. events(事件处理)
javascript
const EventEmitter = require('events');
// 创建事件发射器
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
// 监听事件
myEmitter.on('event', () => {
console.log('事件触发!');
});
// 触发事件
myEmitter.emit('event');
// 带参数的事件
myEmitter.on('data', (data) => {
console.log('收到数据:', data);
});
myEmitter.emit('data', { name: '张三', age: 25 });
// 只触发一次的事件
myEmitter.once('once', () => {
console.log('这个事件只会触发一次');
});
myEmitter.emit('once');
myEmitter.emit('once'); // 不会触发
// 错误处理
myEmitter.on('error', (error) => {
console.error('发生错误:', error.message);
});
myEmitter.emit('error', new Error('这是一个错误'));
// 移除事件监听器
const handler = () => {
console.log('这个监听器会被移除');
};
myEmitter.on('remove', handler);
myEmitter.emit('remove');
myEmitter.removeListener('remove', handler);
myEmitter.emit('remove'); // 不会触发
// 获取监听器数量
console.log('监听器数量:', myEmitter.listenerCount('event'));
// 获取所有监听器
console.log('所有监听器:', myEmitter.listeners('event'));
5. stream(流处理)
javascript
const fs = require('fs');
const { Transform } = require('stream');
// 读取流
const readStream = fs.createReadStream('input.txt', 'utf8');
readStream.on('data', (chunk) => {
console.log('读取数据块:', chunk);
});
readStream.on('end', () => {
console.log('读取完成');
});
readStream.on('error', (error) => {
console.error('读取错误:', error);
});
// 写入流
const writeStream = fs.createWriteStream('output.txt', 'utf8');
writeStream.write('Hello, ');
writeStream.write('Node.js!\n');
writeStream.end('结束写入');
writeStream.on('finish', () => {
console.log('写入完成');
});
writeStream.on('error', (error) => {
console.error('写入错误:', error);
});
// 管道流(复制文件)
const readStream2 = fs.createReadStream('input.txt');
const writeStream2 = fs.createWriteStream('output.txt');
readStream2.pipe(writeStream2);
readStream2.on('end', () => {
console.log('文件复制完成');
});
// 转换流(大写转换)
const upperCaseTransform = new Transform({
transform(chunk, encoding, callback) {
this.push(chunk.toString().toUpperCase());
callback();
}
});
fs.createReadStream('input.txt')
.pipe(upperCaseTransform)
.pipe(fs.createWriteStream('uppercase.txt'));
// 压缩流
const zlib = require('zlib');
const gzip = zlib.createGzip();
fs.createReadStream('input.txt')
.pipe(gzip)
.pipe(fs.createWriteStream('input.txt.gz'));
// 解压流
const gunzip = zlib.createGunzip();
fs.createReadStream('input.txt.gz')
.pipe(gunzip)
.pipe(fs.createWriteStream('uncompressed.txt'));
6. crypto(加密)
javascript
const crypto = require('crypto');
// MD5哈希
const hash1 = crypto.createHash('md5').update('Hello, Node.js!').digest('hex');
console.log('MD5:', hash1);
// SHA256哈希
const hash2 = crypto.createHash('sha256').update('Hello, Node.js!').digest('hex');
console.log('SHA256:', hash2);
// HMAC
const hmac = crypto.createHmac('sha256', 'secret-key');
hmac.update('Hello, Node.js!');
console.log('HMAC:', hmac.digest('hex'));
// AES加密
const algorithm = 'aes-256-cbc';
const key = crypto.randomBytes(32);
const iv = crypto.randomBytes(16);
function encrypt(text) {
const cipher = crypto.createCipheriv(algorithm, key, iv);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
return encrypted;
}
function decrypt(encrypted) {
const decipher = crypto.createDecipheriv(algorithm, key, iv);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
const original = 'Hello, Node.js!';
const encrypted = encrypt(original);
const decrypted = decrypt(encrypted);
console.log('原文:', original);
console.log('加密:', encrypted);
console.log('解密:', decrypted);
// 生成随机数
const randomBytes = crypto.randomBytes(16).toString('hex');
console.log('随机数:', randomBytes);
// 生成UUID
function generateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
console.log('UUID:', generateUUID());
四、NPM包管理
4.1 NPM基础命令
bash
# 初始化项目
npm init
# 初始化项目(使用默认配置)
npm init -y
# 安装依赖
npm install
# 安装指定包
npm install package-name
# 安装到生产环境
npm install package-name --save
# 安装到开发环境
npm install package-name --save-dev
# 全局安装
npm install -g package-name
# 卸载包
npm uninstall package-name
# 更新包
npm update package-name
# 查看已安装的包
npm list
# 查看全局安装的包
npm list -g
# 查看包信息
npm info package-name
# 搜索包
npm search package-name
# 查看过期的包
npm outdated
# 清理缓存
npm cache clean --force
4.2 package.json详解
json
{
"name": "my-nodejs-app",
"version": "1.0.0",
"description": "我的Node.js应用",
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js",
"test": "jest",
"build": "webpack --mode production"
},
"dependencies": {
"express": "^4.18.2",
"mongoose": "^7.0.3",
"dotenv": "^16.0.3"
},
"devDependencies": {
"nodemon": "^2.0.22",
"jest": "^29.5.0",
"webpack": "^5.82.1"
},
"engines": {
"node": ">=16.0.0",
"npm": ">=8.0.0"
},
"author": "张三",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/username/repo.git"
},
"keywords": ["nodejs", "express", "api"]
}
4.3 常用NPM包
express - Web框架
bash
npm install express
javascript
const express = require('express');
const app = express();
// 中间件
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// 路由
app.get('/', (req, res) => {
res.send('Hello, Express!');
});
app.get('/api/users', (req, res) => {
res.json([
{ id: 1, name: '张三' },
{ id: 2, name: '李四' }
]);
});
app.post('/api/users', (req, res) => {
const user = req.body;
user.id = Date.now();
res.status(201).json(user);
});
app.listen(3000, () => {
console.log('Express服务器运行在 http://localhost:3000');
});
nodemon - 自动重启
bash
npm install -D nodemon
json
{
"scripts": {
"dev": "nodemon index.js"
}
}
dotenv - 环境变量
bash
npm install dotenv
javascript
// .env
PORT=3000
DB_HOST=localhost
DB_USER=root
DB_PASS=password
// index.js
require('dotenv').config();
const port = process.env.PORT || 3000;
console.log('服务器端口:', port);
axios - HTTP客户端
bash
npm install axios
javascript
const axios = require('axios');
// GET请求
async function getData() {
try {
const response = await axios.get('https://api.example.com/data');
console.log(response.data);
} catch (error) {
console.error('请求失败:', error);
}
}
// POST请求
async function postData() {
try {
const response = await axios.post('https://api.example.com/users', {
name: '张三',
age: 25
});
console.log(response.data);
} catch (error) {
console.error('请求失败:', error);
}
}
mongoose - MongoDB ODM
bash
npm install mongoose
javascript
const mongoose = require('mongoose');
// 连接数据库
mongoose.connect('mongodb://localhost:27017/mydatabase', {
useNewUrlParser: true,
useUnifiedTopology: true
});
// 定义模型
const UserSchema = new mongoose.Schema({
name: { type: String, required: true },
age: { type: Number, required: true },
email: { type: String, required: true, unique: true }
});
const User = mongoose.model('User', UserSchema);
// 创建用户
async function createUser() {
const user = new User({
name: '张三',
age: 25,
email: 'zhangsan@example.com'
});
await user.save();
console.log('用户创建成功:', user);
}
// 查询用户
async function findUsers() {
const users = await User.find({ age: { $gte: 20 } });
console.log('查询结果:', users);
}
// 更新用户
async function updateUser(id) {
const user = await User.findByIdAndUpdate(
id,
{ age: 26 },
{ new: true }
);
console.log('用户更新成功:', user);
}
// 删除用户
async function deleteUser(id) {
await User.findByIdAndDelete(id);
console.log('用户删除成功');
}
五、Express框架详解
5.1 Express基础
javascript
const express = require('express');
const app = express();
// 中间件配置
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static('public'));
// 路由
app.get('/', (req, res) => {
res.send('Hello, Express!');
});
app.get('/about', (req, res) => {
res.send('关于我们');
});
// 路由参数
app.get('/users/:id', (req, res) => {
const userId = req.params.id;
res.send(`用户ID: ${userId}`);
});
// 查询参数
app.get('/search', (req, res) => {
const keyword = req.query.keyword;
res.send(`搜索关键词: ${keyword}`);
});
// POST请求
app.post('/users', (req, res) => {
const user = req.body;
res.status(201).json(user);
});
// 启动服务器
app.listen(3000, () => {
console.log('服务器运行在 http://localhost:3000');
});
5.2 路由器
javascript
const express = require('express');
const router = express.Router();
// 用户路由
router.get('/', (req, res) => {
res.json([
{ id: 1, name: '张三' },
{ id: 2, name: '李四' }
]);
});
router.get('/:id', (req, res) => {
const userId = req.params.id;
res.json({ id: userId, name: '张三' });
});
router.post('/', (req, res) => {
const user = req.body;
user.id = Date.now();
res.status(201).json(user);
});
router.put('/:id', (req, res) => {
const userId = req.params.id;
const user = req.body;
res.json({ id: userId, ...user });
});
router.delete('/:id', (req, res) => {
const userId = req.params.id;
res.json({ message: `用户${userId}已删除` });
});
module.exports = router;
// 在主应用中使用
const userRoutes = require('./routes/users');
app.use('/api/users', userRoutes);
5.3 中间件
javascript
// 日志中间件
const logger = (req, res, next) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
next();
};
// 认证中间件
const auth = (req, res, next) => {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ message: '未授权' });
}
// 验证token
next();
};
// 错误处理中间件
const errorHandler = (err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ message: '服务器错误' });
};
// 使用中间件
app.use(logger);
app.use('/api/protected', auth);
// 错误处理
app.use(errorHandler);
5.4 模板引擎
javascript
// 安装EJS
npm install ejs
// 配置模板引擎
app.set('view engine', 'ejs');
app.set('views', './views');
// 渲染模板
app.get('/', (req, res) => {
res.render('index', {
title: '我的网站',
users: [
{ name: '张三', age: 25 },
{ name: '李四', age: 30 }
]
});
});
// views/index.ejs
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
</head>
<body>
<h1><%= title %></h1>
<ul>
<% users.forEach(user => { %>
<li><%= user.name %> - <%= user.age %>岁</li>
<% }) %>
</ul>
</body>
</html>
六、数据库操作
6.1 MongoDB
javascript
const mongoose = require('mongoose');
// 连接数据库
mongoose.connect('mongodb://localhost:27017/myapp');
// 定义Schema
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
age: { type: Number, default: 0 },
createdAt: { type: Date, default: Date.now }
});
// 创建Model
const User = mongoose.model('User', userSchema);
// CRUD操作
// 创建
async function createUser() {
const user = new User({
name: '张三',
email: 'zhangsan@example.com',
age: 25
});
await user.save();
return user;
}
// 读取
async function getUsers() {
const users = await User.find({ age: { $gte: 18 } })
.sort({ age: -1 })
.limit(10);
return users;
}
async function getUserById(id) {
const user = await User.findById(id);
return user;
}
// 更新
async function updateUser(id, updates) {
const user = await User.findByIdAndUpdate(
id,
updates,
{ new: true, runValidators: true }
);
return user;
}
// 删除
async function deleteUser(id) {
await User.findByIdAndDelete(id);
}
6.2 MySQL
javascript
const mysql = require('mysql2/promise');
// 创建连接池
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'password',
database: 'myapp',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
});
// CRUD操作
// 创建
async function createUser(user) {
const sql = 'INSERT INTO users (name, email, age) VALUES (?, ?, ?)';
const [result] = await pool.execute(sql, [user.name, user.email, user.age]);
return result.insertId;
}
// 读取
async function getUsers() {
const [rows] = await pool.execute('SELECT * FROM users WHERE age >= ?', [18]);
return rows;
}
async function getUserById(id) {
const [rows] = await pool.execute('SELECT * FROM users WHERE id = ?', [id]);
return rows[0];
}
// 更新
async function updateUser(id, updates) {
const sql = 'UPDATE users SET name = ?, email = ?, age = ? WHERE id = ?';
const [result] = await pool.execute(sql, [updates.name, updates.email, updates.age, id]);
return result.affectedRows > 0;
}
// 删除
async function deleteUser(id) {
const sql = 'DELETE FROM users WHERE id = ?';
const [result] = await pool.execute(sql, [id]);
return result.affectedRows > 0;
}
// 事务
async function transferMoney(fromId, toId, amount) {
const connection = await pool.getConnection();
try {
await connection.beginTransaction();
// 扣款
await connection.execute(
'UPDATE users SET balance = balance - ? WHERE id = ?',
[amount, fromId]
);
// 加款
await connection.execute(
'UPDATE users SET balance = balance + ? WHERE id = ?',
[amount, toId]
);
await connection.commit();
return true;
} catch (error) {
await connection.rollback();
throw error;
} finally {
connection.release();
}
}
七、实战项目:RESTful API
7.1 项目结构
my-api/
├── config/
│ └── database.js
├── models/
│ └── User.js
├── routes/
│ └── users.js
├── middleware/
│ └── auth.js
├── controllers/
│ └── userController.js
├── app.js
├── .env
└── package.json
7.2 实现代码
javascript
// package.json
{
"name": "my-api",
"version": "1.0.0",
"main": "app.js",
"scripts": {
"start": "node app.js",
"dev": "nodemon app.js"
},
"dependencies": {
"express": "^4.18.2",
"mongoose": "^7.0.3",
"dotenv": "^16.0.3",
"bcryptjs": "^2.4.3",
"jsonwebtoken": "^9.0.0"
},
"devDependencies": {
"nodemon": "^2.0.22"
}
}
// .env
PORT=3000
MONGODB_URI=mongodb://localhost:27017/myapi
JWT_SECRET=your-secret-key
// config/database.js
const mongoose = require('mongoose');
const connectDB = async () => {
try {
await mongoose.connect(process.env.MONGODB_URI);
console.log('MongoDB连接成功');
} catch (error) {
console.error('MongoDB连接失败:', error);
process.exit(1);
}
};
module.exports = connectDB;
// models/User.js
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true
},
email: {
type: String,
required: true,
unique: true,
trim: true,
lowercase: true
},
password: {
type: String,
required: true,
minlength: 6
},
role: {
type: String,
enum: ['user', 'admin'],
default: 'user'
}
}, {
timestamps: true
});
// 密码加密
userSchema.pre('save', async function(next) {
if (!this.isModified('password')) {
return next();
}
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
next();
});
// 密码验证
userSchema.methods.comparePassword = async function(candidatePassword) {
return await bcrypt.compare(candidatePassword, this.password);
};
module.exports = mongoose.model('User', userSchema);
// controllers/userController.js
const User = require('../models/User');
const jwt = require('jsonwebtoken');
// 生成JWT
const generateToken = (userId) => {
return jwt.sign({ userId }, process.env.JWT_SECRET, {
expiresIn: '7d'
});
};
// 注册
exports.register = async (req, res) => {
try {
const { name, email, password } = req.body;
// 检查用户是否已存在
const existingUser = await User.findOne({ email });
if (existingUser) {
return res.status(400).json({ message: '邮箱已被注册' });
}
// 创建用户
const user = new User({ name, email, password });
await user.save();
// 生成token
const token = generateToken(user._id);
res.status(201).json({
message: '注册成功',
token,
user: {
id: user._id,
name: user.name,
email: user.email,
role: user.role
}
});
} catch (error) {
res.status(500).json({ message: '服务器错误', error: error.message });
}
};
// 登录
exports.login = async (req, res) => {
try {
const { email, password } = req.body;
// 查找用户
const user = await User.findOne({ email });
if (!user) {
return res.status(401).json({ message: '邮箱或密码错误' });
}
// 验证密码
const isMatch = await user.comparePassword(password);
if (!isMatch) {
return res.status(401).json({ message: '邮箱或密码错误' });
}
// 生成token
const token = generateToken(user._id);
res.json({
message: '登录成功',
token,
user: {
id: user._id,
name: user.name,
email: user.email,
role: user.role
}
});
} catch (error) {
res.status(500).json({ message: '服务器错误', error: error.message });
}
};
// 获取用户信息
exports.getProfile = async (req, res) => {
try {
const user = await User.findById(req.userId).select('-password');
if (!user) {
return res.status(404).json({ message: '用户不存在' });
}
res.json(user);
} catch (error) {
res.status(500).json({ message: '服务器错误', error: error.message });
}
};
// 更新用户信息
exports.updateProfile = async (req, res) => {
try {
const { name, email } = req.body;
const user = await User.findByIdAndUpdate(
req.userId,
{ name, email },
{ new: true, runValidators: true }
).select('-password');
if (!user) {
return res.status(404).json({ message: '用户不存在' });
}
res.json({ message: '更新成功', user });
} catch (error) {
res.status(500).json({ message: '服务器错误', error: error.message });
}
};
// middleware/auth.js
const jwt = require('jsonwebtoken');
const auth = async (req, res, next) => {
try {
const token = req.header('Authorization')?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ message: '未提供认证token' });
}
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.userId = decoded.userId;
next();
} catch (error) {
res.status(401).json({ message: '无效的token' });
}
};
module.exports = auth;
// routes/users.js
const express = require('express');
const router = express.Router();
const { register, login, getProfile, updateProfile } = require('../controllers/userController');
const auth = require('../middleware/auth');
router.post('/register', register);
router.post('/login', login);
router.get('/profile', auth, getProfile);
router.put('/profile', auth, updateProfile);
module.exports = router;
// app.js
require('dotenv').config();
const express = require('express');
const connectDB = require('./config/database');
const userRoutes = require('./routes/users');
const app = express();
// 连接数据库
connectDB();
// 中间件
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// 路由
app.use('/api/users', userRoutes);
// 错误处理
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ message: '服务器错误' });
});
// 启动服务器
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器运行在 http://localhost:${PORT}`);
});
八、Node.js最佳实践
8.1 错误处理
javascript
// 使用异步函数和try-catch
async function asyncOperation() {
try {
const result = await someAsyncOperation();
return result;
} catch (error) {
console.error('操作失败:', error);
throw error;
}
}
// 错误处理中间件
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({
message: '服务器错误',
error: process.env.NODE_ENV === 'development' ? err.message : {}
});
});
// 未捕获的Promise拒绝
process.on('unhandledRejection', (reason, promise) => {
console.error('未处理的Promise拒绝:', reason);
});
// 未捕获的异常
process.on('uncaughtException', (error) => {
console.error('未捕获的异常:', error);
process.exit(1);
});
8.2 安全性
javascript
// 使用helmet
const helmet = require('helmet');
app.use(helmet());
// 使用cors
const cors = require('cors');
app.use(cors());
// 输入验证
const { body, validationResult } = require('express-validator');
app.post('/users', [
body('name').notEmpty().withMessage('姓名不能为空'),
body('email').isEmail().withMessage('邮箱格式不正确'),
body('password').isLength({ min: 6 }).withMessage('密码至少6位')
], (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// 处理请求
});
// 环境变量
require('dotenv').config();
// 限制请求频率
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100 // 限制100次请求
});
app.use(limiter);
8.3 性能优化
javascript
// 启用Gzip压缩
const compression = require('compression');
app.use(compression());
// 使用缓存
const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 600 }); // 10分钟缓存
app.get('/api/data', async (req, res) => {
const cacheKey = 'data';
const cachedData = cache.get(cacheKey);
if (cachedData) {
return res.json(cachedData);
}
const data = await fetchData();
cache.set(cacheKey, data);
res.json(data);
});
// 使用连接池
const pool = mysql.createPool({
connectionLimit: 10,
// 其他配置
});
// 使用集群(多进程)
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
} else {
// 启动服务器
app.listen(3000);
}
8.4 日志记录
javascript
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.simple()
}));
}
// 使用日志
logger.info('服务器启动');
logger.error('发生错误', error);
九、部署和运维
9.1 PM2进程管理
bash
# 安装PM2
npm install -g pm2
# 启动应用
pm2 start app.js
# 启动应用并指定名称
pm2 start app.js --name my-app
# 列出所有进程
pm2 list
# 查看日志
pm2 logs
# 重启应用
pm2 restart my-app
# 停止应用
pm2 stop my-app
# 删除应用
pm2 delete my-app
# 监控
pm2 monit
# 保存进程列表
pm2 save
# 设置开机自启
pm2 startup
9.2 Docker容器化
dockerfile
# Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]
bash
# 构建镜像
docker build -t my-nodejs-app .
# 运行容器
docker run -p 3000:3000 my-nodejs-app
yaml
# docker-compose.yml
version: '3'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- MONGODB_URI=mongodb://mongo:27017/myapp
depends_on:
- mongo
mongo:
image: mongo:latest
ports:
- "27017:27017"
volumes:
- mongo-data:/data/db
volumes:
mongo-data:
bash
# 使用docker-compose启动
docker-compose up -d
十、总结
Node.js是一个功能强大、生态丰富的后端开发平台。通过本文的学习,你应该掌握了:
- Node.js的基础概念和安装配置
- 模块系统(CommonJS和ES6)
- 核心模块的使用(fs、http、path、events等)
- NPM包管理和常用包
- Express框架的详细使用
- 数据库操作(MongoDB、MySQL)
- 完整的RESTful API项目实战
- Node.js最佳实践(错误处理、安全性、性能优化)
- 部署和运维(PM2、Docker)
学习建议:
- 多动手实践,创建自己的项目
- 深入理解Node.js的异步编程模型
- 学习TypeScript提高代码质量
- 关注Node.js的最新发展
- 参与开源项目,学习优秀的代码
- 掌握监控和调试技巧
Node.js的学习是一个持续的过程,随着技术的发展,Node.js也在不断进化。保持学习的热情,不断提升自己的技能,你一定能成为一名优秀的Node.js开发者!
希望这篇Node.js详解教程对你有所帮助!如果你有任何问题或建议,欢迎留言讨论。持续学习,不断进步,让我们一起在后端开发的道路上越走越远!