从零上手 Node.js:模块、HTTP 服务器、文件系统与事件机制实战指南
2026/9/15 11:59:11 网站建设 项目流程

从零上手 Node.js:模块、HTTP 服务器、文件系统与事件机制实战指南

【免费下载链接】curriculumThe open curriculum for learning web development项目地址: https://gitcode.com/GitHub_Trending/cu/curriculum

导读

本篇指南以 Node.js 的入门学习路径为主线,系统讲解在 Node.js 中运行脚本、创建与使用模块、用 HTTP 模块搭建基础 Web 服务器、用 fs 模块读写文件、用 URL 类解析地址以及使用 EventEmitter 处理事件等核心能力。读完本篇后,你将能够脱离浏览器、纯用 JavaScript 编写可在本机或服务器上运行的后端程序,并为课程末尾的 Basic Informational Site(基础信息站点)项目打下完整基础。本文基于仓库中的 getting_started.md 一课展开,并辅以同一课程路径下的 introduction_what_is_nodeJS.md、environment_variables.md、debugging_node.md 与 project_basic_informational_site.md 进行深化。

前置准备:为什么先学 JavaScript 再学 Node

在动手之前需要明确一个基本事实:Node.js 本质上就是 JavaScript。正如 getting_started.md 开头强调的,只有具备基本的 JavaScript 基础,才能理解 Node 的语法与行为。Node 并不是一门新语言,而是一个把 JavaScript 从浏览器中"解放"出来的运行时。

这一点在 introduction_what_is_nodeJS.md 中有更详细的阐述:JavaScript 最初被设计为只能在浏览器内运行,这导致它无法编写网站以外的任何程序。Node 的出现让开发者可以用 JavaScript 完成 Ruby、PHP、C#、Python 等传统服务端语言能够完成的几乎所有事情——在本地计算机或服务器上直接运行 JavaScript,而无需经过浏览器。

为了支撑服务端场景,Node 在浏览器 JavaScript 之外额外提供了一些能力,其中包括:

  • 读取和写入本地文件;
  • 创建 HTTP 连接;
  • 监听网络请求。

本章的学习目标非常明确(来自原文的 Lesson overview):

  • 说明 Node.js 的常见用途;
  • 创建和使用模块(既包括内置模块,也包括用户自定义模块);
  • 使用 HTTP 模块搭建基础 Web 服务器;
  • 从 Node.js 中读取、创建、更新和删除文件;
  • 使用 URL 模块解析网址并将其拆分为可读部分;
  • 理解如何使用 NPM;
  • 创建、触发并监听自己的事件。

理解 Node 的异步与事件驱动模型

在编写第一段服务端代码前,有必要先理解 Node 的运行哲学。Node 官网将其定义为"异步事件驱动的 JavaScript 运行时",这个定义可以拆解为两层含义:

  • 异步(asynchronous):写代码时不需要预测每一行代码的精确执行顺序。程序被组织为一系列小函数,这些函数在特定事件(如网络请求)发生时被调用;
  • 事件驱动(event driven):程序在等待某个过程完成时,并不阻塞后续代码,而是"坐等"事件发生,事件一到就触发对应的回调函数。

introduction_what_is_nodeJS.md 给出了一个非常直观的例子:假设程序需要"从文件读取文本 → 打印文本内容"以及"查询数据库用户列表 → 按年龄过滤用户"。如果同步执行,必须逐条等待;而异步执行时,Node 会在等待文件读取的过程中立即启动数据库查询,两个任务并行推进,谁先完成谁先触发后续回调,程序员无需关心二者的完成顺序。

这与前端 JavaScript 中使用addEventListener等待鼠标点击或键盘按键的思路几乎一致,区别只在于 Node 监听的事件是网络请求和数据库查询这类服务端事件,而这一机制正是通过**回调函数(callbacks)**实现的。

下面这段代码就是整个课程中最早的 Node 服务端示例:

http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/html'}); res.end('Hello World!'); }).listen(8080);

这段代码创建了一个服务器,并声明:"每当收到一个网络请求,就执行这个回调函数"。回调函数向客户端返回文本Hello World!。在浏览器中访问正确的地址和端口,就能在屏幕上看到这行文本。它也是下一篇 HTTP 模块的主角。

在终端中运行 Node.js 脚本

入门的第一步是学会从命令行运行 Node 脚本。与浏览器中通过<script>标签加载 JavaScript 不同,Node 脚本是独立的文件,通过node命令直接执行:

node your-script.js

例如,把上面的 HTTP 示例保存为server.js,然后在项目目录下执行node server.js,Node 就会启动这个脚本并保持监听。需要中断运行时,在终端按下Ctrl + C即可。

从仓库中可以看到,后续课程大量使用这种"命令行驱动"的工作流:例如 introduction_to_express.md 中通过node app.js启动 Express 服务器,environment_variables.md 中则展示了NODE_ENV=prod node index.js这类带环境变量的运行方式。这说明"运行 Node 脚本"是贯穿整个 Node.js 课程的基础操作。

内置模块与用户自定义模块

Node 的功能是以模块为单位组织的。模块化意味着你可以把可复用的代码封装成独立文件,在需要的地方引入。Node 的模块体系分为两类:

  • 内置模块:Node 自带,如httpfsurleventspath等,直接require即可使用,无需安装;
  • 用户自定义模块:你自己编写并导出的代码文件,通过module.exports导出、通过require('./path')引入。

一个典型的用户自定义模块如下:

// math.js function add(a, b) { return a + b; } module.exports = { add };
// index.js const { add } = require('./math'); console.log(add(2, 3)); // 5

这种"按需引入、职责分离"的组织方式,正是后面课程中拆分路由、控制器等代码结构的基础。接下来几节将逐一介绍入门必备的四个内置模块。

HTTP 模块:创建基础 Web 服务器

HTTP 模块是 Node 服务端编程的核心。它提供了创建 HTTP 服务器和发起 HTTP 请求的能力。

使用 http.createServer 创建服务器

http.createServer是 HTTP 模块中最常用的方法:它创建一个 HTTP 服务器,并接收一个处理函数(handler),该处理函数会在每次收到请求时被执行。如原文所述:

createServer方法创建一个 HTTP 服务器,该服务器接收每次收到请求时都会执行的处理函数。

服务器对象本身具备listen方法,用于指定监听端口。综合前文,一个最小可运行的服务器如下:

const http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/html'}); res.end('Hello World!'); }).listen(8080);

这里有几个要点:

  • req是请求对象,包含客户端发来的方法、URL、请求头等信息;
  • res是响应对象,res.writeHead用于写入状态码(如200表示成功)和响应头(如Content-Type: text/html),res.end结束响应并发送正文;
  • .listen(8080)让服务器监听本机 8080 端口,访问http://localhost:8080即可看到响应内容。

发起 HTTP 请求

服务端场景不仅需要接收请求,也常常需要主动向外发起请求(例如调用第三方 API)。Node 提供了fetch(基于浏览器同款 Fetch API)等能力用于发起 HTTP 请求。在入门阶段,先掌握"接收请求并返回响应"即可,主动发起请求会在后续 working_with_apis.md 等课程中进一步深化。

从回调到路由分发

回调函数中的req.url可以用来区分不同路径,从而分发到不同页面。这正是本课程末尾项目(Basic Informational Site)的核心逻辑:

  • 访问/返回index.html
  • 访问/about返回about.html
  • 访问/contact-me返回contact-me.html
  • 其他所有路径返回404.html

不过仅靠http模块手写路由会比较啰嗦,这也是后续引入 Express 框架的原因(详见 introduction_to_express.md 中对"请求的旅程"和中间件机制的讲解)。入门阶段先用原生http模块亲手实现一遍,才能深刻理解框架替你做了什么。

文件系统模块:读写文件

服务端程序离不开文件操作,Node 的fs(File System)模块正是为此而生。它是 Node 中"重度使用"的模块之一,覆盖文件的读取、创建、更新和删除。

写入文件

写入文件最常用的方法是fs.writeFile。注意fs模块的大多数方法都同时提供异步(回调风格)与同步(带Sync后缀)两种版本,异步版本不会阻塞事件循环:

const fs = require('fs'); // 异步写入:文件不存在则创建,存在则覆盖 fs.writeFile('message.txt', 'Hello Node!', (err) => { if (err) throw err; console.log('文件已写入'); });

对应地,fs.appendFile可以在已有文件末尾追加内容,适合日志类场景;fs.rename用于重命名文件;fs.unlink用于删除文件。它们共同构成了原文所说的"读取、创建、更新和删除"四类操作:

// 追加内容(创建/更新) fs.appendFile('message.txt', '\nSecond line', (err) => { if (err) throw err; }); // 重命名(更新) fs.rename('message.txt', 'renamed.txt', (err) => { if (err) throw err; }); // 删除 fs.unlink('renamed.txt', (err) => { if (err) throw err; });

读取文件

读取文件使用fs.readFile,回调的第二个参数即为文件内容:

const fs = require('fs'); fs.readFile('message.txt', 'utf8', (err, data) => { if (err) { console.error(err); return; } console.log(data); });

传入'utf8'编码参数后,data就是可直接使用的字符串;省略该参数时,data会是 Buffer(二进制缓冲区)。在 Basic Informational Site 项目中,fs.readFile正是"按 URL 读取对应 HTML 文件并返回给客户端"的关键手段。

URL 类:解析网址为可读部分

处理网络请求时,经常需要把网址拆解为协议、主机名、路径、查询参数等可读部分。Node 的url模块提供 WHATWG 标准的URL类来完成这项工作:

const myURL = new URL('https://example.org:8080/path/to/page?name=ferret&age=3'); console.log(myURL.hostname); // 'example.org' console.log(myURL.port); // '8080' console.log(myURL.pathname); // '/path/to/page' console.log(myURL.search); // '?name=ferret&age=3' console.log(myURL.searchParams.get('name')); // 'ferret'

URL实例暴露了hostnameportpathnamesearchsearchParams等大量只读属性,searchParams还提供了getsetappenddelete等方法,方便读写查询参数。原文建议"亲手运行这些代码示例看看它是如何工作的",因为这正是把"请求的 URL"映射到"要返回的页面文件"这一路由逻辑的数据基础。

Events 模块:创建、触发与监听事件

Node 是事件驱动的,而事件机制的底层实现就是events模块中的EventEmitter(事件发射器)类。你可以创建自己的事件发射器,用on(或addListener)注册监听器,用emit触发事件:

const EventEmitter = require('events'); const myEmitter = new EventEmitter(); // 监听 'greet' 事件 myEmitter.on('greet', (name) => { console.log(`Hello, ${name}!`); }); // 触发 'greet' 事件,并携带参数 myEmitter.emit('greet', 'Node');

事件发射器可以同时注册多个监听器,事件触发时它们会按注册顺序依次执行;emit传入的额外参数会被透传给监听器函数。理解EventEmitter之后,你会发现自己写的 HTTP 服务器本质上也是一个"在收到请求时触发回调"的事件机制——http.createServer的回调、fs.readFile的回调,都是事件驱动模型的具体体现。

NPM 与 package.json

NPM(Node Package Manager)随 Node 一同安装,是 Node 生态的包管理工具,用于安装、管理和发布第三方依赖。入门阶段需要掌握三个基本命令:

# 初始化项目,生成 package.json npm init -y # 安装依赖(-D 表示作为开发依赖) npm install <package-name> npm install -D <package-name> # 运行 package.json scripts 中定义的脚本 npm run <script-name>

package.json是项目的"身份档案",记录项目名称、版本、脚本和依赖。本仓库根目录的 package.json 就是一个典型实例,其中定义了lintfixtest三个 npm script,并通过devDependencies引入markdownlint-cli2作为开发依赖:

{ "name": "curriculum", "version": "1.0.0", "scripts": { "lint": "markdownlint-cli2", "fix": "markdownlint-cli2 --fix", "test": "node --test" }, "license": "CC BY-NC-SA 4.0", "devDependencies": { "markdownlint-cli2": "^0.23.2" } }

这个文件展示了三个可迁移到任何 Node 项目的实践:用scripts固化常用命令、用devDependencies区分仅开发期需要的工具、用语义化版本范围(^0.23.2)声明依赖版本。在 introduction_to_express.md 中,Express 的安装也正是通过npm init -ynpm install express完成的,可见 NPM 是后续所有第三方框架学习的前提。

实战演练:Basic Informational Site 项目

学习完上述模块后,就到了动手环节。本课程末尾的项目 project_basic_informational_site.md 要求用 Node 创建一个包含 4 个页面的基础信息站点。项目规格如下:

  1. 在项目目录中创建 4 个 HTML 文件:
    • index.html
    • about.html
    • contact-me.html
    • 404.html
  2. 创建 Node 服务器文件index.js,编写代码根据 URL 返回对应页面:
    • http://localhost:8080→ 返回index.html
    • http://localhost:8080/about→ 返回about.html
    • http://localhost:8080/contact-me→ 返回contact-me.html
    • 访问上述以外的任何路径 → 返回404.html

结合本篇所学,一个可行的实现骨架是:用http.createServer接收请求,读取req.url判断路径,再用fs.readFile读取对应的 HTML 文件并通过res返回;路径不匹配时回退到404.html,同时通过res.writeHead设置Content-Type: text/html和合适的 HTTP 状态码。项目刻意强调"页面内容并不重要,不需要花时间美化",目的是把全部注意力放在 Node 的服务端逻辑上。

完成这个项目后,如果你觉得手写路由和文件分发比较繁琐,可以对照 introduction_to_express.md 的作业要求,安装 Express 用几个app.get()重写一遍同样的项目——这将是理解"框架为什么存在"的最佳对比实验。

进阶补充:环境变量与调试

入门之后,还有两个与 Node 日常开发强相关的能力值得一并掌握。

用环境变量隔离配置

environment_variables.md 介绍了如何让不同环境(本机开发、云端部署)使用不同的配置。环境变量可以通过以下方式加载:

  • 直接在启动命令中赋值:NODE_ENV=prod node index.js
  • 使用 shell 的export命令写入当前会话;
  • 使用.env文件(Node 内置支持,通过node --env-file=.env index.jsprocess.loadEnvFile()加载);
  • 使用 dotenv 等第三方库(老项目中常见)。

在代码中通过process.env读取,例如:

const PORT = process.env.PORT || 3000;

注意:环境变量的值永远是字符串,需要数值或布尔时须自行转换;包含敏感信息的.env文件必须加入.gitignore,绝不可提交到仓库。

用调试器定位问题

debugging_node.md 则强调:服务端代码不能依赖浏览器 DevTools,而应学会使用 VS Code 内置的 Node 调试器,在编辑器中直接打断点、观察变量。VS Code 的 JavaScript Debug Terminal 是最便捷的启动方式之一,这是从"会写 Node"走向"能调好 Node"的关键工具。

知识自检

读完本篇后,试着回答以下问题(对应原文档的 Knowledge check):

  1. 什么是文件系统模块?你会如何使用它、为什么使用它?
  2. 如何在终端中运行一个 Node.js 脚本?
  3. http.createServer的回调函数在什么时机被执行?
  4. 如何用fs模块读取和写入文件?
  5. URL类如何拿到网址的路径和查询参数?
  6. 如何创建自己的EventEmitter并监听、触发事件?
  7. 为什么说 Node 是"异步事件驱动"的运行时?

如果这些问题都能清晰作答,并且能独立完成 Basic Informational Site 项目,你就已经跨过了 Node.js 入门的门槛,可以带着对回调、模块、事件和文件操作的理解,进入 Express 与更复杂的后端开发学习了。

【免费下载链接】curriculumThe open curriculum for learning web development项目地址: https://gitcode.com/GitHub_Trending/cu/curriculum

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询