Skip to content

Node.js 介绍与历史

什么是 Node.js?

Node.js 是一个基于 Chrome V8 引擎的JavaScript 运行时环境,它让 JavaScript 能够在服务端运行。简单来说,Node.js 就是让 JavaScript 脱离浏览器限制,在服务器上执行的工具。

Node.js 的革命性意义

在 Node.js 出现之前,JavaScript 主要局限在浏览器中运行:

html
<!-- 浏览器环境中的JavaScript -->
<script>
  // 只能在浏览器中运行,功能受限
  function showMessage() {
    alert("Hello World!");
    console.log("浏览器控制台输出");
  }

  // 无法访问文件系统、网络、操作系统资源
</script>

Node.js 改变了这一切:

javascript
// 服务端JavaScript (Node.js环境)
const http = require("http"); // HTTP服务器
const fs = require("fs"); // 文件系统
const path = require("path"); // 路径处理
const os = require("os"); // 操作系统信息

// 创建HTTP服务器
const server = http.createServer((req, res) => {
  // 读取文件系统
  const filePath = path.join(__dirname, "index.html");

  fs.readFile(filePath, (err, data) => {
    if (err) {
      res.writeHead(500, { "Content-Type": "text/plain" });
      res.end("服务器错误");
    } else {
      res.writeHead(200, { "Content-Type": "text/html" });
      res.end(data);
    }
  });
});

// 启动服务器
server.listen(3000, () => {
  console.log("服务器运行在 http://localhost:3000");
  console.log(`操作系统: ${os.type()} ${os.release()}`);
});

这就是 JavaScript 的"越狱":从浏览器沙盒中解放出来,拥有了服务端编程的所有能力!

Node.js 的发展历史

2008-2009 年:诞生之初

Node.js 的故事始于 2008 年,当时 Ryan Dahl 在雅虎工作,对现有的服务端技术感到不满:

javascript
// 传统服务器模型的问题(PHP/Java示例)
function handleRequest(request, response) {
  // 1. 每个请求创建一个新线程
  // 2. 阻塞I/O操作,线程被占用等待
  // 3. 内存消耗大,并发能力有限
  // 4. 上下文切换开销高

  // 模拟数据库查询(阻塞操作)
  const result = database.query("SELECT * FROM users WHERE id = 1");

  // 模拟文件读取(阻塞操作)
  const content = fileSystem.readFile("template.html");

  // 只有在所有操作完成后,这个线程才能处理下一个请求
  response.send(content.replace("{user}", result.name));
}

Ryan Dahl 意识到,对于 Web 应用来说,大部分时间都花在等待 I/O 操作上(数据库查询、文件读写、网络请求等),而不是 CPU 计算。他设计了一个全新的模型:

javascript
// Node.js的非阻塞I/O模型
const fs = require("fs");
const http = require("http");

function handleRequest(request, response) {
  // 非阻塞I/O操作,使用回调函数
  fs.readFile("template.html", (err, template) => {
    if (err) {
      response.writeHead(500);
      return response.end("文件读取错误");
    }

    // 异步数据库查询
    database.query("SELECT * FROM users WHERE id = 1", (err, result) => {
      if (err) {
        response.writeHead(500);
        return response.end("数据库查询错误");
      }

      response.writeHead(200, { "Content-Type": "text/html" });
      response.end(template.replace("{user}", result.name));
    });
  });

  // 函数立即返回,线程不会被阻塞
  // 可以同时处理成千上万个并发请求
}

2010-2012 年:快速发展

Node.js 社区迅速成长,NPM(Node Package Manager)的诞生更是推动了生态系统的爆炸式发展:

bash
# 早期的包管理
npm install express      # Web框架
npm install socket.io     # 实时通信
npm install mongoose     # MongoDB驱动
npm install async        # 异步流程控制
npm install request      # HTTP客户端

2013 年至今:企业级应用

Node.js 从"玩具"成长为"主力":

javascript
// 现代Node.js应用示例(完整的企业级API)
import express from "express";
import { connect } from "mongoose";
import { config } from "dotenv";
import helmet from "helmet";
import cors from "cors";
import rateLimit from "express-rate-limit";

// 环境配置
config();

// 数据库连接
await connect(process.env.MONGODB_URI);

// Express应用
const app = express();

// 安全中间件
app.use(helmet());
app.use(cors());

// 限流
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15分钟
  max: 100, // 限制每个IP最多100个请求
});
app.use(limiter);

// JSON解析
app.use(express.json());

// API路由
app.get("/api/users", async (req, res) => {
  try {
    const users = await User.find().limit(10);
    res.json({ success: true, data: users });
  } catch (error) {
    res.status(500).json({
      success: false,
      message: "服务器错误",
    });
  }
});

// 错误处理中间件
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({
    success: false,
    message: "服务器内部错误",
  });
});

// 启动服务器
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`服务器运行在端口 ${PORT}`);
});

Node.js 的核心特性

1. 单线程与事件循环

Node.js 采用单线程模型,但通过事件循环实现高并发:

javascript
// 事件循环的直观理解
console.log("开始");

setTimeout(() => {
  console.log("定时器回调 - 宏任务");
}, 0);

Promise.resolve().then(() => {
  console.log("Promise回调 - 微任务");
});

console.log("结束");

// 输出顺序:
// 开始
// 结束
// Promise回调 - 微任务
// 定时器回调 - 宏任务

事件循环的工作原理:

javascript
// 简化的事件循环模型
class EventLoop {
  constructor() {
    this.callStack = []; // 调用栈
    this.taskQueue = []; // 宏任务队列
    this.microtaskQueue = []; // 微任务队列
  }

  run() {
    while (true) {
      // 1. 执行调用栈中的所有代码
      while (this.callStack.length > 0) {
        const task = this.callStack.pop();
        this.execute(task);
      }

      // 2. 执行所有微任务
      while (this.microtaskQueue.length > 0) {
        const microtask = this.microtaskQueue.shift();
        this.execute(microtask);
      }

      // 3. 执行一个宏任务
      if (this.taskQueue.length > 0) {
        const macrotask = this.taskQueue.shift();
        this.callStack.push(macrotask);
      }
    }
  }
}

2. 非阻塞 I/O

非阻塞 I/O 是 Node.js 高性能的关键:

javascript
// 阻塞I/O vs 非阻塞I/O示例

// 阻塞方式(传统)
function blockingFileRead(filename) {
  const data = fs.readFileSync(filename); // 阻塞主线程
  console.log("文件读取完成");
  return data;
}

// 非阻塞方式(Node.js)
function nonBlockingFileRead(filename, callback) {
  fs.readFile(filename, (err, data) => {
    // 异步回调
    if (err) {
      callback(err);
    } else {
      console.log("文件读取完成");
      callback(null, data);
    }
  });

  console.log("函数立即返回,主线程不被阻塞");
}

// Promise方式
function promiseFileRead(filename) {
  return new Promise((resolve, reject) => {
    fs.readFile(filename, (err, data) => {
      if (err) reject(err);
      else resolve(data);
    });
  });
}

// Async/Await方式(现代)
async function asyncFileRead(filename) {
  try {
    const data = await fs.promises.readFile(filename);
    console.log("文件读取完成");
    return data;
  } catch (err) {
    console.error("文件读取失败:", err);
    throw err;
  }
}

3. 模块系统

Node.js 实现了强大的模块系统:

javascript
// CommonJS模块系统(传统)
// user.js
const database = require("./database");

class UserService {
  constructor() {
    this.db = database;
  }

  async createUser(userData) {
    const user = await this.db.collection("users").insertOne(userData);
    return user;
  }

  async getUserById(id) {
    return await this.db.collection("users").findOne({ _id: id });
  }
}

// 导出
module.exports = UserService;

// ES6模块系统(现代)
// user-service.js
import { connectToDatabase } from "./database.js";

export class UserService {
  #db; // 私有字段

  constructor() {
    this.#db = connectToDatabase();
  }

  async createUser(userData) {
    const user = await this.#db.collection("users").insertOne(userData);
    return user;
  }

  async getUserById(id) {
    return await this.#db.collection("users").findOne({ _id: id });
  }
}

// 默认导出
export default UserService;

// 在其他文件中使用
// main.js
import UserService, { createUser } from "./user-service.js";
import express from "express";

const app = express();
const userService = new UserService();

app.post("/users", async (req, res) => {
  try {
    const user = await userService.createUser(req.body);
    res.status(201).json({ success: true, data: user });
  } catch (error) {
    res.status(500).json({ success: false, message: error.message });
  }
});

4. 丰富的生态系统

NPM 是世界上最大的软件包注册表,提供了海量的模块:

javascript
// package.json - 项目依赖管理
{
  "name": "my-nodejs-app",
  "version": "1.0.0",
  "description": "Node.js应用程序示例",
  "main": "src/index.js",
  "type": "module", // 使用ES6模块
  "scripts": {
    "start": "node src/index.js",
    "dev": "nodemon src/index.js",
    "test": "jest",
    "build": "babel src -d dist"
  },
  "dependencies": {
    "express": "^4.18.0",        // Web框架
    "mongoose": "^6.0.0",       // MongoDB ODM
    "jsonwebtoken": "^8.5.1",     // JWT认证
    "bcryptjs": "^2.4.3",        // 密码加密
    "dotenv": "^10.0.0",         // 环境变量
    "helmet": "^4.6.0",         // 安全中间件
    "cors": "^2.8.5",           // 跨域资源共享
    "compression": "^1.7.4"      // 响应压缩
  },
  "devDependencies": {
    "nodemon": "^2.0.15",       // 开发时自动重启
    "jest": "^27.5.1",          // 测试框架
    "@babel/cli": "^7.17.0",     // Babel CLI
    "@babel/core": "^7.17.0",    // Babel核心
    "@babel/preset-env": "^7.16.0" // Babel预设
  }
}

Node.js 与其他服务端技术的对比

Node.js vs 传统服务端语言

特性Node.jsPHPJavaPython
执行模型单线程事件循环多进程/多线程多线程多线程
内存使用中等中等
并发性能I/O 密集型优秀有限中等
CPU 密集型较弱中等优秀中等
学习曲线平缓(JavaScript 开发者)简单陡峭简单
生态极其丰富成熟成熟成熟
适合场景Web API、实时应用、微服务Web 网站、小型应用企业应用、大型系统数据科学、Web 应用

实际性能对比

javascript
// 性能测试:并发HTTP请求处理
// Node.js测试代码
const http = require("http");
const port = 3000;

const server = http.createServer((req, res) => {
  // 模拟I/O操作
  setTimeout(() => {
    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(JSON.stringify({ message: "Hello from Node.js!" }));
  }, 10); // 10ms延迟模拟数据库查询
});

server.listen(port, () => {
  console.log(`Node.js服务器运行在端口 ${port}`);
});
php
// 同样的PHP实现(阻塞模型)
<?php
// php内置服务器启动命令: php -S localhost:8080
function handleRequest() {
    // 模拟阻塞的I/O操作
    usleep(10000); // 10ms延迟模拟数据库查询

    header('Content-Type: application/json');
    echo json_encode(['message' => 'Hello from PHP!']);
}

handleRequest();
?>

性能对比结果(概念性)

  • Node.js:单线程可以处理 10,000+并发连接
  • PHP(Apache mod_php):每个连接一个线程,大约 200-300 并发
  • Java(Tomcat):线程池模式,可以处理 1000-2000 并发
  • Python(Flask/Gunicorn):多进程模式,可以处理 1000-1500 并发

Node.js 的设计哲学

1. JavaScript 全栈

Node.js 让 JavaScript 真正成为全栈语言:

javascript
// 前端代码 (浏览器)
class UserComponent {
  constructor() {
    this.userService = new UserService("/api/users");
  }

  async loadUsers() {
    try {
      const users = await this.userService.getAll();
      this.renderUsers(users);
    } catch (error) {
      this.showError(error.message);
    }
  }
}

// 后端代码 (Node.js)
class UserService {
  constructor(route) {
    this.route = route;
    this.setupRoutes();
  }

  setupRoutes() {
    app.get(this.route, this.getAll.bind(this));
    app.post(this.route, this.create.bind(this));
    app.get(`${this.route}/:id`, this.getById.bind(this));
  }

  async getAll(req, res) {
    try {
      const users = await User.find().limit(50);
      res.json({
        success: true,
        data: users.map((user) => ({
          id: user._id,
          name: user.name,
          email: user.email,
          createdAt: user.createdAt,
        })),
      });
    } catch (error) {
      res.status(500).json({
        success: false,
        message: error.message,
      });
    }
  }
}

2. 微服务架构的天然选择

Node.js 的轻量级特性使其成为微服务的理想选择:

javascript
// 用户服务微服务
// user-service.js
import express from "express";
import { createClient } from "redis";

const app = express();
const redis = createClient({ url: process.env.REDIS_URL });

// 连接Redis缓存
await redis.connect();

// 获取用户信息(带缓存)
app.get("/users/:id", async (req, res) => {
  const userId = req.params.id;

  try {
    // 1. 先从缓存获取
    const cachedUser = await redis.get(`user:${userId}`);

    if (cachedUser) {
      return res.json({
        success: true,
        source: "cache",
        data: JSON.parse(cachedUser),
      });
    }

    // 2. 缓存未命中,从数据库获取
    const user = await User.findById(userId);

    if (!user) {
      return res.status(404).json({
        success: false,
        message: "用户不存在",
      });
    }

    // 3. 缓存结果(5分钟过期)
    await redis.setEx(`user:${userId}`, 300, JSON.stringify(user));

    res.json({
      success: true,
      source: "database",
      data: user,
    });
  } catch (error) {
    res.status(500).json({
      success: false,
      message: error.message,
    });
  }
});

// 健康检查端点
app.get("/health", (req, res) => {
  res.json({
    service: "user-service",
    status: "healthy",
    timestamp: new Date().toISOString(),
  });
});

const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
  console.log(`用户服务运行在端口 ${PORT}`);
});

3. 异步优先的设计

Node.js 从根本上就是异步的:

javascript
// 同步代码在Node.js中的问题
function syncProblems() {
  console.log("开始处理");

  // 这些操作会阻塞主线程
  const data1 = fs.readFileSync("file1.txt"); // 阻塞
  const data2 = fs.readFileSync("file2.txt"); // 阻塞
  const data3 = fs.readFileSync("file3.txt"); // 阻塞

  // 在Node.js中应该这样做
  console.log("同步处理完成");
}

// 异步优先的解决方案
function asyncSolutions() {
  console.log("开始处理");

  // 并行处理多个文件
  const readFile = promisify(fs.readFile);

  Promise.all([
    readFile("file1.txt"),
    readFile("file2.txt"),
    readFile("file3.txt"),
  ])
    .then(([data1, data2, data3]) => {
      console.log("异步处理完成");
      // 处理所有文件数据
    })
    .catch((err) => {
      console.error("文件读取失败:", err);
    });

  console.log("函数立即返回,不阻塞主线程");
}

// 使用async/await的更清晰方案
async function asyncAwaitSolution() {
  try {
    console.log("开始处理");

    // 并行读取
    const [data1, data2, data3] = await Promise.all([
      fs.promises.readFile("file1.txt"),
      fs.promises.readFile("file2.txt"),
      fs.promises.readFile("file3.txt"),
    ]);

    console.log("异步处理完成");
    return { data1, data2, data3 };
  } catch (err) {
    console.error("文件读取失败:", err);
    throw err;
  }
}

Node.js 的应用场景

1. Web API 服务器

Node.js 非常适合构建 RESTful API:

javascript
// 完整的API服务器示例
import express from "express";
import { body, validationResult } from "express-validator";
import jwt from "jsonwebtoken";
import bcrypt from "bcryptjs";

const app = express();
app.use(express.json());

// 中间件:JWT认证
const authenticateToken = (req, res, next) => {
  const authHeader = req.headers["authorization"];
  const token = authHeader && authHeader.split(" ")[1];

  if (!token) {
    return res.status(401).json({ message: "缺少访问令牌" });
  }

  jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
    if (err) return res.status(403).json({ message: "无效的令牌" });
    req.user = user;
    next();
  });
};

// 用户注册路由
app.post(
  "/api/register",
  [
    body("email").isEmail().normalizeEmail(),
    body("password").isLength({ min: 6 }),
    body("name").trim().isLength({ min: 2 }),
  ],
  async (req, res) => {
    // 验证输入
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({
        success: false,
        errors: errors.array(),
      });
    }

    const { email, password, name } = req.body;

    try {
      // 检查用户是否存在
      const existingUser = await User.findOne({ email });
      if (existingUser) {
        return res.status(409).json({
          success: false,
          message: "该邮箱已被注册",
        });
      }

      // 加密密码
      const salt = await bcrypt.genSalt(10);
      const hashedPassword = await bcrypt.hash(password, salt);

      // 创建用户
      const user = new User({
        name,
        email,
        password: hashedPassword,
      });

      await user.save();

      // 生成JWT
      const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, {
        expiresIn: "24h",
      });

      res.status(201).json({
        success: true,
        message: "注册成功",
        data: {
          user: {
            id: user._id,
            name: user.name,
            email: user.email,
          },
          token,
        },
      });
    } catch (error) {
      console.error("注册失败:", error);
      res.status(500).json({
        success: false,
        message: "服务器错误",
      });
    }
  }
);

// 受保护的路由
app.get("/api/profile", authenticateToken, async (req, res) => {
  try {
    const user = await User.findById(req.user.userId).select("-password");

    if (!user) {
      return res.status(404).json({
        success: false,
        message: "用户不存在",
      });
    }

    res.json({
      success: true,
      data: user,
    });
  } catch (error) {
    res.status(500).json({
      success: false,
      message: "服务器错误",
    });
  }
});

2. 实时应用

Socket.io 让 Node.js 成为实时应用的首选:

javascript
// 聊天应用服务器
import { createServer } from "http";
import { Server } from "socket.io";
import express from "express";

const app = express();
const server = createServer(app);
const io = new Server(server, {
  cors: {
    origin: "*",
    methods: ["GET", "POST"],
  },
});

// 存储在线用户
const onlineUsers = new Map();

io.on("connection", (socket) => {
  console.log(`用户连接: ${socket.id}`);

  // 用户加入聊天
  socket.on("join", (userData) => {
    onlineUsers.set(socket.id, userData);

    // 广播用户上线
    socket.broadcast.emit("user-joined", {
      user: userData,
      onlineCount: onlineUsers.size,
    });

    // 发送当前在线用户列表
    socket.emit("users-list", Array.from(onlineUsers.values()));
  });

  // 处理聊天消息
  socket.on("send-message", (messageData) => {
    const message = {
      id: Date.now(),
      text: messageData.text,
      sender: messageData.sender,
      timestamp: new Date().toISOString(),
    };

    // 广播消息给所有用户(除了发送者)
    socket.broadcast.emit("receive-message", message);

    // 发送回发送者,确认消息已发送
    socket.emit("message-sent", message);
  });

  // 私人消息
  socket.on("private-message", (data) => {
    const targetSocket = [...onlineUsers.entries()].find(
      ([id, user]) => user.id === data.receiverId
    )?.[0];

    if (targetSocket) {
      io.to(targetSocket).emit("private-message", {
        text: data.text,
        sender: data.sender,
        timestamp: new Date().toISOString(),
      });
    }
  });

  // 用户断开连接
  socket.on("disconnect", () => {
    const userData = onlineUsers.get(socket.id);

    if (userData) {
      onlineUsers.delete(socket.id);

      // 广播用户下线
      socket.broadcast.emit("user-left", {
        user: userData,
        onlineCount: onlineUsers.size,
      });
    }

    console.log(`用户断开连接: ${socket.id}`);
  });
});

const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
  console.log(`聊天服务器运行在端口 ${PORT}`);
});

总结

Node.js 通过将 JavaScript 带到服务端,彻底改变了 Web 开发的格局。它的事件驱动、非阻塞 I/O 模型特别适合处理大量并发连接的 Web 应用,成为了现代 Web 开发的重要工具。

Node.js 的核心价值

  1. 统一的开发语言:JavaScript 贯穿前后端,减少技术栈复杂度
  2. 优秀的并发性能:事件循环模型擅长处理 I/O 密集型任务
  3. 丰富的生态系统:NPM 提供了海量的模块和工具
  4. 微服务友好:轻量级特性使其成为微服务的理想选择
  5. 全栈 JavaScript:实现代码复用和团队技能统一

适用场景总结

  • Web API 服务器:RESTful API、GraphQL 服务
  • 实时应用:聊天应用、在线游戏、实时数据推送
  • 微服务架构:轻量级、独立部署的服务
  • 工具和脚本:构建工具、自动化脚本、CLI 工具
  • 流处理:文件处理、数据流处理

Node.js 不是银弹,它最擅长处理 I/O 密集型任务,对于 CPU 密集型计算可能不是最佳选择。但在大多数 Web 应用场景中,Node.js 都提供了优秀的性能和开发体验。

随着 Node.js 的不断发展和生态系统的成熟,它已经从一个实验性项目成长为构建企业级应用的可靠选择。掌握 Node.js 不仅能提升你的技术能力,更能让你在 Web 开发中拥有更多的选择和灵活性。

上次更新时间: