Node.js完全指南:从入门到精通

一、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系统
  1. 访问Node.js官网:https://nodejs.org/
  2. 下载LTS版本(推荐)或Current版本
  3. 运行安装程序,按提示完成安装
  4. 验证安装:
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是一个功能强大、生态丰富的后端开发平台。通过本文的学习,你应该掌握了:

  1. Node.js的基础概念和安装配置
  2. 模块系统(CommonJS和ES6)
  3. 核心模块的使用(fs、http、path、events等)
  4. NPM包管理和常用包
  5. Express框架的详细使用
  6. 数据库操作(MongoDB、MySQL)
  7. 完整的RESTful API项目实战
  8. Node.js最佳实践(错误处理、安全性、性能优化)
  9. 部署和运维(PM2、Docker)

学习建议

  • 多动手实践,创建自己的项目
  • 深入理解Node.js的异步编程模型
  • 学习TypeScript提高代码质量
  • 关注Node.js的最新发展
  • 参与开源项目,学习优秀的代码
  • 掌握监控和调试技巧

Node.js的学习是一个持续的过程,随着技术的发展,Node.js也在不断进化。保持学习的热情,不断提升自己的技能,你一定能成为一名优秀的Node.js开发者!


希望这篇Node.js详解教程对你有所帮助!如果你有任何问题或建议,欢迎留言讨论。持续学习,不断进步,让我们一起在后端开发的道路上越走越远!

相关推荐
吴声子夜歌2 小时前
Node.js——操作MySQL数据库
数据库·mysql·node.js
清风细雨_林木木5 小时前
Node.js 和 Python 的关系
node.js
吴声子夜歌5 小时前
Node.js——Express框架
node.js·express
吴声子夜歌7 小时前
Node.js——异常处理
node.js
FreeBuf_9 小时前
谷歌将Axios npm供应链攻击归因于朝鲜APT组织UNC1069
前端·npm·node.js
阿正的梦工坊10 小时前
pnpm和npm前端包管理工具有什么不同?
前端·npm·node.js
叶半欲缺10 小时前
Node.js 安装教程
node.js
吴声子夜歌10 小时前
Node.js——Web模板引擎
前端·node.js
雪碧聊技术10 小时前
linux下载node.js(这里面已经包含了npm)
npm·node.js
摇滚侠1 天前
搭建前端开发环境 安装 nodejs 设置淘宝镜像 最简化最标准版本 不使用 NVM NVM 高版本无法安装低版本 nodejs
java·开发语言·node.js