Day 1:Node.js 入门指南——从环境搭建到第一个HTTP服务器
今日学习目标
- 理解 Node.js 的核心概念与特点。
- 完成开发环境搭建并运行第一个脚本。
- 掌握 Node.js 的模块化开发(
require和module.exports)。 - 用原生
http模块创建一个简单的 HTTP 服务器。
🌍 Node.js 简介
1. 什么是 Node.js?
Node.js 是一个基于 Chrome V8 引擎 的 JavaScript 运行时,用于构建高性能的服务器端应用。它的核心特点包括:
- 事件驱动:通过事件循环(Event Loop)处理并发请求。
- 非阻塞 I/O:异步操作文件、网络等,避免线程阻塞。
- 单线程但支持多进程:可通过
cluster模块利用多核 CPU。
2. Node.js 与浏览器 JavaScript 的区别
- 无 BOM/DOM:Node.js 没有
window或document对象,专注于系统级 API(如文件操作、网络服务)。 - 模块化系统:使用 CommonJS 规范(
require和exports),而非浏览器的 ES Modules。
环境搭建
1. 安装 Node.js
- 下载 LTS 版本(推荐企业级使用):Node.js 官网。
-
下载完成之后双击,一路点击next即可完成安装
-
验证安装:
node -v # 查看 Node.js 版本
npm -v # 查看 npm 版本
- 我的版本参考
node -v
--v22.16.0
npm -v
--10.9.2
2. 编辑器配置
推荐使用 VS Code,安装插件:
- ESLint(代码规范检查)。
- REST Client(测试 HTTP 接口)。
1. 第一个 Node.js 脚本
创建 index.js:
console.log("这是我的第一个node.js");
运行:
node index.js
✅ 输出:
这是我的第一个node.js
VS Code操作截图:
4. 文件的读写操作
读取文件内容
我的目录结构:
├── a.txt
└── read.js
Node.js 采用 CommonJS 模块系统:我们需要读取一个文件,这里可以在我们的官方文档:文件操作去查阅相关的API
往下翻我们能看到-
fsPromises.readFile(path[, options]),点进去是相关的使用案例
下面是我的代码:
1.方法一:Promise
// 引入Node.js的文件系统Promise版本模块(使用node:协议明确指定核心模块)
// readFile是一个返回Promise的异步文件读取方法
const { readFile } = require('node:fs/promises');
// 引入Node.js的路径处理模块
// resolve方法用于将路径片段解析为绝对路径
const { resolve } = require('node:path');
// 定义异步函数读取并打印文件内容
async function logFile() {
try {
// 使用resolve方法将相对路径'./a.txt'解析为绝对路径
// 这样可以避免路径拼接错误,确保总能找到正确位置的文件
const filePath = resolve('./a.txt');
// 使用await异步读取文件内容
// 参数说明:
// filePath - 要读取的文件路径
// { encoding: 'utf8' } - 以UTF-8编码读取文件内容,直接返回字符串而不是Buffer
const contents = await readFile(filePath, { encoding: 'utf8' });
// 打印文件内容,使用模板字符串拼接了文件名和文件内容
console.log(`${filePath}文件的内容是:${contents}`);
} catch (err) {
// 捕获并处理可能出现的错误
// 例如:文件不存在、权限问题等
// 这里只打印错误的基本信息,避免暴露过多细节
console.error(err.message);
}
}
// 调用异步函数
// 注意:在顶层使用await需要Node.js 14.8.0+或在ES模块中
logFile();
// 补充说明:
// 1. 使用node:fs/promises而不是回调风格的fs模块,代码更简洁
2. 方法二:CallBack
//导入FS模块
// 引入Node.js内置的fs(文件系统)模块
// fs模块提供了文件操作的API
const fs = require('fs')
// 使用fs.readFile()方法异步读取文件内容
// 参数说明:
// 1. './a.txt' - 要读取的文件路径(相对路径)
// 2. 'utf-8' - 指定以UTF-8编码读取文件,返回字符串而不是Buffer
// 3. function(err, data) - 回调函数,Node.js标准的错误优先回调风格
fs.readFile('./a.txt', 'utf-8', function(err, data) {
// 错误处理
// 注意:正确语法是if (err),这里缺少括号
if (err) {
// 如果读取出错,打印错误信息
console.error(err)
// 建议在这里return,避免继续执行后面的代码
return
}
// 如果读取成功,打印文件内容
// 由于指定了'utf-8'编码,data是字符串类型
console.log(data)
})
运行:`node read.js`
```text
a.txt文件的内容是:hahaha
我们刚刚实现了读操作,那么试一下写操作呢:
1. 方法一:Promise
// 导入Node.js的文件系统Promise API中的writeFile方法
// 使用node:前缀明确指定使用Node.js核心模块
import { writeFile } from 'node:fs/promises';
// 导入Node.js的路径处理模块中的resolve方法
// 用于将相对路径解析为绝对路径
import { resolve } from 'node:path';
// 定义异步函数用于写入文件
async function writeToFile() {
// 创建AbortController实例,用于控制异步操作的取消
const controller = new AbortController();
// 从controller中解构出signal对象
// signal用于传递给异步操作,以便可以取消操作
const { signal } = controller;
try {
// 使用resolve方法将相对路径'./a.txt'解析为绝对路径
// 这样可以确保在不同工作目录下都能找到正确的文件位置
const filePath = resolve('./a.txt');
// 使用await异步写入文件
// 参数说明:
// 1. filePath - 要写入的文件路径
// 2. '写入的值' - 要写入的内容
// 3. {
// encoding: 'utf-8' - 指定使用UTF-8编码写入文件
// signal - 传递AbortSignal以便可以取消操作
// }
await writeFile(filePath, '写入的值', {
encoding: 'utf-8',
signal
});
// 文件写入成功后打印成功信息
console.log('文件写入成功');
} catch (err) {
// 捕获并处理可能出现的错误
if (err.code === 'ABORT_ERR') {
// 如果是操作被中止的错误
console.log('写入操作被中止');
} else {
// 其他类型的错误
console.error('写入错误:', err);
}
}
}
// 调用异步函数
// 注意:这里没有调用controller.abort(),所以操作不会被提前中止
// 如果需要取消操作,可以在适当的位置调用controller.abort()
writeToFile();
2. 方法二:CallBack
const fs = require('fs')
fs.writeFile('./a.txt', '写进去的值', (err) => {
if(!err) {
console.log('写进去了');
}
})
执行后的结果如图所示:
3.留下一个问题,如果我们想向文件里追加新的数据,应该怎么做呢?
5.模块化开发
模块化编程是一种将程序分解为独立、可重用模块的软件开发方法。
📖 推荐阅读:
- Node.js 模块机制详解(《深入浅出 Node.js》第 2 章)。
1. 为什么需要引入模块化?
举个例子:假如我们现在有两个js文件被同时引入在一个html文件中,并且这两个文件中定义了一个重名变量,那么当我们的html文件中使用到这个变量时,会发生什么呢?
例如:m1.js,如下:
var username = 'zhangsan';
m2.js,如下:
var username = 'wangwu';
index.html,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./m1.js"></script>
<script src="./m2.js"></script>
<script>
alert(username)
</script>
</head>
<body>
</body>
</html>
在浏览器中打开,结果如图:
可见,alert弹出的是最后一次导入的,这对于团队合作来说及其不利,试想一下:你的username和同事的username冲突了,只是因为他的代码在你后面被引用了,然后你调用的时候,username就变成了他设定的值。
在很长的一段时间里,大家都是通过自执行函数来解决这一问题,直到后来社区出现了模块化的解决方案:
- CommonJS ---服务器端
- ADM和UMD ---浏览器端
后来,在ES6发布之后,带来了官方的解决方案
- ECMAScript Module
2. CommonJS(Node.js默认)
// 导出
module.exports = { functionA, variableB };
// 导入
const { functionA } = require('./moduleA');
//例如:
const { readFile } = require('node:fs/promises');
const { resolve } = require('node:path');
3. ES Modules(现代标准)
// 导出
export function functionA() {}
export const variableB = 42;
// 默认导出
export default mainFunction;
// 导入
import { functionA } from './moduleA.js';
import mainFunc from './mainModule.js';
// 例如:
import { writeFile } from 'node:fs/promises';
import { resolve } from 'node:path';
5. 用 http 模块搭建 HTTP 服务器
创建一个 server.js:
const http = require('http');
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello, Node.js Server!');
});
server.listen(3000, () => {
console.log('Server running at http://localhost:3000');
});
✅ 运行:
node server.js
❌ 常见错误:"Port already in use"
-
查找并终止占用端口的进程:
lsof -i :3000 # 查找 kill -9 PID # 终止 -
或者更改应用监听端口
📌 访问:http://localhost:3000,你会看到 Hello, Node.js Server!。
📖 推荐阅读:
- B站《Node.js HTTP 服务器教程》(2025 最新版)。
📹 视频教程: