使用 Node.js 搭建 HTTP 静态服务器 —— 从 CommonJS 到 ES6 模块化

29 阅读6分钟

Node.js 自诞生以来,凭借其非阻塞 I/O 和事件驱动的特性,迅速成为后端开发的重要工具。它不仅支持 JavaScript 在浏览器之外运行,还提供了丰富的内置模块,帮助开发者快速构建高性能的网络应用。

在本篇博客中,我们将深入讲解如何使用 Node.js 构建一个基础的 HTTP 静态资源服务器,并详细探讨:

  • requireimport:CommonJS 与 ES6 模块化的区别
  • HTTP 协议的基本原理
  • 如何创建和监听 HTTP 服务器
  • 路由匹配与请求处理机制
  • 文件系统操作(fs)与路径拼接(path)
  • 错误处理与状态码设置
  • URL 结构与查询参数理解

此外,我们还会对一段完整的 HTTP 服务器代码进行 详细解析,确保你掌握每一个细节。


🧩 什么是 Node.js?

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境,允许我们在服务器端执行 JavaScript 代码。

💡 JS 不仅是前端语言,在 Node.js 环境下,JavaScript 也可以作为后端语言在命令行中运行。

Node.js 适合中小型项目快速开发,尤其在高并发场景中表现优异,例如聊天服务器、API 接口服务等。


📦 Node.js 的模块化系统

模块化是现代编程语言的重要能力之一。Node.js 支持两种主要的模块化方案:

1. CommonJS(传统方式)

这是 Node.js 最早使用的模块化方案,使用 require() 来引入模块,以同步方式加载依赖。

const fs = require('fs');

2. ES6 Module(更先进的模块化方式)

ES6(ECMAScript 2015)提出了模块化标准,使用 importexport 实现模块化,更加符合现代 Web 开发趋势。

import fs from 'fs';

⚠️ 注意:Node.js 默认使用的是 CommonJS 模块化方式。如果要使用 ES6 模块化,需要将文件扩展名改为 .mjs,或者在 package.json 中设置 "type": "module"

Node.js 正逐步向 CommonJS 说再见

随着 Node.js 22 版本的发布,官方对 ES6 模块的支持已经非常成熟。未来,Node.js 将更倾向于推荐使用 ES6 模块化方案,CommonJS 虽然仍会支持,但不再是首选。


🛠 创建 HTTP 服务器

Node.js 提供了内置的 http 模块,可以轻松创建 HTTP 服务器。

使用 CommonJS 方式创建服务器

const http = require('http');

const server = http.createServer((req, res) => {
    res.end('Hello HTTP Server');
});

server.listen(8080);

使用 ES6 Module 方式创建服务器(需保存为 .mjs 文件)

// server.mjs
import http from 'http';

const server = http.createServer((req, res) => {
    res.end('Hello HTTP Server (ES6)');
});

server.listen(1234);

✅ 小贴士:使用 node server.mjs 命令运行 ES6 模块化代码。


🌐 端口、进程与 IP 地址的关系

  • IP 地址:标识网络中的某台设备,例如本地测试常用的 127.0.0.1
  • 域名:如 localhost,通常映射到 127.0.0.1
  • 端口:每个端口对应一个服务或进程,例如:
    • 3306 → MySQL 数据库
    • 8080 → 自定义 HTTP 服务
  • 一台设备可以同时运行多个服务,监听不同的端口。

⚠️ 建议避免使用特殊端口(如 0~1023),这些端口通常被操作系统保留使用。


🏗️ 构建静态资源服务器详解

我们的目标是搭建一个能响应 HTML、CSS、JS 等静态资源的服务器。

所需模块介绍

模块功能
http创建 HTTP 服务器
fs文件系统操作,读取文件内容
path处理路径拼接问题

示例目录结构

project-root/
├── public/
│   ├── index.html
│   ├── style.css
│   └── script.js
└── server.js

📜 完整代码解析

下面是对代码的完整解析,包含每一部分的功能说明:

// 引入 Node.js 内置的核心模块
const http = require('http'); // 用于创建 HTTP 服务器
const fs = require('fs');     // 用于读写文件系统
const path = require('path'); // 用于处理路径拼接问题

// 创建 HTTP 服务器实例
const server = http.createServer((req, res) => {

    // 请求方法 + URL 组成路由
    if (req.method === 'GET' && (req.url === '/' || req.url === '/index.html')) {
        // 拼接文件路径,确保兼容不同操作系统
        const filePath = path.join(__dirname, 'public', 'index.html');

        // 异步读取文件内容
        fs.readFile(filePath, (err, content) => {
            if (err) {
                // 发生错误时返回 500 服务器内部错误
                res.writeHead(500); 
                res.end('Server Error');
                return;
            }
            // 成功读取文件后返回 200 状态码和 HTML 内容
            res.writeHead(200, { 'Content-Type': 'text/html' });
            res.end(content);
        });
    }

    // 处理 /style.css 请求
    else if (req.method === 'GET' && req.url === '/style.css') {
        const filePath = path.join(__dirname, 'public', 'style.css');

        fs.readFile(filePath, (err, content) => {
            if (err) {
                res.writeHead(500);
                res.end('Server Error');
                return;
            }
            // 返回 CSS 文件,设置 Content-Type 为 text/css
            res.writeHead(200, { 'Content-Type': 'text/css' });
            res.end(content);
        });
    }

    // 处理 /script.js 请求
    else if (req.method === 'GET' && req.url === '/script.js') {
        const filePath = path.join(__dirname, 'public', 'script.js');

        fs.readFile(filePath, (err, content) => {
            if (err) {
                res.writeHead(500);
                res.end('Server Error');
                return;
            }
            // 返回 JS 文件,设置 Content-Type 为 text/javascript
            res.writeHead(200, { 'Content-Type': 'text/javascript' });
            res.end(content);
        });
    }

    // 其他请求返回 404
    else {
        // 对于不支持的路径,返回 404 Not Found
        res.writeHead(404, { 'Content-Type': 'text/plain' });
        res.end('Not Found');
    }
});

// 启动服务器并监听 8080 端口
server.listen(8080, () => {
    console.log('Server is running on http://localhost:8080');
});

🔍 关于 URL 结构的说明

完整的 URL 包含以下几个部分:

http://localhost:8080/style.css?a=1&b=2
↑       ↑      ↑        ↑         ↑
协议     域名    端口     路径     查询参数
  • 协议(Protocol):httphttps
  • 域名(Domain):如 localhost,代表本地机器
  • 端口(Port):如 8080
  • 路径(Path):如 /style.css
  • 查询字符串(Query String):如 ?a=1&b=2

🧱 后端的本质:资源的定位与响应

HTTP 是一种“请求-响应”协议。客户端通过 URL 发送请求,服务器根据 Method + URL 定位资源,并返回相应的内容。

在本例中,我们实现了最基本的“静态资源服务”,也就是将服务器上的文件原样返回给客户端。


🛡 错误处理的重要性

  • 5xx 系列状态码表示服务器错误,如 500 Internal Server Error
  • 我们在每次读取文件时都加入了错误判断逻辑,保证即使发生异常,也能返回友好的提示信息,而不是让客户端陷入等待。

📲 启动服务器并访问页面

  1. 确保你的项目结构正确,且 public/ 目录下包含对应的 HTML、CSS、JS 文件。
  2. 在终端执行以下命令启动服务器:
node server.js
  1. 打开浏览器访问:
http://localhost:8080

你将看到你的首页内容,并成功加载 CSS 和 JS 文件。


📚 总结

本文全面讲解了如何使用 Node.js 构建一个简单的 HTTP 静态资源服务器,涵盖以下核心知识点:

✅ Node.js 是什么?
✅ CommonJS 与 ES6 模块化的区别
✅ 如何创建 HTTP 服务器
✅ 模块化方案的选择(.mjs 文件)
✅ 静态资源服务的工作原理
✅ 路由匹配与请求处理
✅ 内容类型(Content-Type)设置
✅ 错误处理机制
✅ URL 结构解析
✅ 端口、进程、线程之间的关系

如果你喜欢这篇文章,欢迎点赞、收藏并分享给更多想入门 Node.js 的朋友!


📌 最终提醒:如果你希望尝试 ES6 模块化,请将文件保存为 .mjs 格式,并使用最新版本的 Node.js(建议 v22+)进行测试。