摘要:本文是 Node.js 服务器开发的入门指南,涵盖环境安装、版本管理、模块化系统、npm 包管理、核心模块使用和第一个 HTTP 服务器。通过本文档,你将掌握 Node.js 的基础知识,为后续进阶开发打下坚实基础。
关键词:Node.js、环境安装、npm、模块化、CommonJS、ES Modules、HTTP服务器
适合人群:有 JavaScript 基础、想要学习服务器端开发的开发者
预计完成时间:90 分钟(含所有实践环节)
文章目录
- [实践 1:环境安装与配置](#实践 1:环境安装与配置)
-
- [1.1 Node.js 安装](#1.1 Node.js 安装)
- [1.2 nvm 版本管理](#1.2 nvm 版本管理)
- [1.3 npm 包管理器配置](#1.3 npm 包管理器配置)
- [实践 2:模块化系统](#实践 2:模块化系统)
-
- [2.1 CommonJS 模块](#2.1 CommonJS 模块)
- [2.2 ES Modules 模块](#2.2 ES Modules 模块)
- [2.3 核心模块 vs 第三方模块](#2.3 核心模块 vs 第三方模块)
- [实践 3:核心模块使用](#实践 3:核心模块使用)
-
- [3.1 fs 文件系统模块](#3.1 fs 文件系统模块)
- [3.2 path 路径模块](#3.2 path 路径模块)
- [3.3 events 事件模块](#3.3 events 事件模块)
- [实践 4:HTTP 服务器](#实践 4:HTTP 服务器)
-
- [4.1 创建基础 HTTP 服务器](#4.1 创建基础 HTTP 服务器)
- [4.2 处理 POST 请求](#4.2 处理 POST 请求)
- [4.3 静态文件服务器](#4.3 静态文件服务器)
- [实践 5:项目初始化与配置](#实践 5:项目初始化与配置)
-
- [5.1 package.json 详解](#5.1 package.json 详解)
- [5.2 环境变量配置](#5.2 环境变量配置)
- [5.3 nodemon 自动重启](#5.3 nodemon 自动重启)
- 常见问题排查
-
- [Q1:node 命令找不到?](#Q1:node 命令找不到?)
- [Q2:npm install 速度慢?](#Q2:npm install 速度慢?)
- Q3:端口被占用?
- [Q4:require 找不到模块?](#Q4:require 找不到模块?)
- [Q5:ES Modules 语法报错?](#Q5:ES Modules 语法报错?)
- 学习建议
- 官方资源
实践 1:环境安装与配置
1.1 Node.js 安装
目标:正确安装 Node.js 并验证安装成功。
步骤 1:下载 Node.js
访问官网:https://nodejs.org/
- LTS 版本(长期支持版):生产环境推荐,稳定性高
- Current 版本(最新版):包含最新特性,适合开发测试
步骤 2:安装(Windows)
- 下载
.msi安装包 - 双击运行,按提示完成安装
- 安装时勾选 "Add to PATH"(添加到环境变量)
步骤 3:验证安装
bash
# 查看 Node.js 版本
node -v
# 输出:v18.17.0 或更高
# 查看 npm 版本
npm -v
# 输出:9.6.7 或更高
步骤 4:第一个 Node.js 程序
创建 hello.js:
javascript
console.log('Hello, Node.js!');
运行:
bash
node hello.js
# 输出:Hello, Node.js!
1.2 nvm 版本管理
目标:使用 nvm 管理多个 Node.js 版本,适应不同项目需求。
步骤 1:安装 nvm-windows
下载地址:https://github.com/coreybutler/nvm-windows/releases
下载 nvm-setup.exe 并安装。
步骤 2:常用命令
bash
# 查看可安装版本
nvm list available
# 安装指定版本
nvm install 18.17.0
nvm install 20.5.0
# 查看已安装版本
nvm list
# 切换版本
nvm use 18.17.0
# 设置默认版本
nvm alias default 18.17.0
步骤 3:验证切换
bash
nvm use 18.17.0
node -v
# 输出:v18.17.0
nvm use 20.5.0
node -v
# 输出:v20.5.0
1.3 npm 包管理器配置
目标:配置 npm 提升包安装速度,设置全局安装路径。
步骤 1:配置国内镜像
bash
# 使用淘宝镜像
npm config set registry https://registry.npmmirror.com
# 验证配置
npm config get registry
# 输出:https://registry.npmmirror.com
步骤 2:配置全局安装路径
bash
# 创建全局安装目录
mkdir C:\Users\你的用户名\nodejs\global
mkdir C:\Users\你的用户名\nodejs\cache
# 配置 npm
npm config set prefix "C:\Users\你的用户名\nodejs\global"
npm config set cache "C:\Users\你的用户名\nodejs\cache"
# 将全局安装路径添加到系统环境变量 PATH
步骤 3:常用 npm 命令
bash
# 初始化项目
npm init
npm init -y # 跳过问答,使用默认配置
# 安装依赖
npm install express # 安装到当前项目
npm install -g nodemon # 全局安装
npm install lodash --save # 安装并保存到 dependencies
npm install jest --save-dev # 安装到 devDependencies
# 查看依赖
npm list # 查看当前项目依赖
npm list -g # 查看全局依赖
# 更新依赖
npm update # 更新所有依赖
npm update express # 更新指定依赖
# 卸载依赖
npm uninstall express
npm uninstall -g nodemon
实践 2:模块化系统
2.1 CommonJS 模块
目标:掌握 Node.js 默认的模块化规范。
步骤 1:导出模块
创建 math.js:
javascript
// 方式 1:导出单个函数
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
// 导出多个函数
module.exports = {
add,
subtract
};
步骤 2:导入模块
创建 app.js:
javascript
// 导入自定义模块(需要相对路径)
const math = require('./math');
console.log(math.add(5, 3)); // 输出:8
console.log(math.subtract(5, 3)); // 输出:2
步骤 3:导出单个值
创建 config.js:
javascript
// 导出单个对象
module.exports = {
port: 3000,
host: 'localhost',
database: {
host: '127.0.0.1',
port: 5432,
name: 'myapp'
}
};
使用:
javascript
const config = require('./config');
console.log(config.port); // 输出:3000
步骤 4:模块缓存机制
javascript
// counter.js
let count = 0;
module.exports = {
increment: () => ++count,
getCount: () => count
};
测试:
javascript
const counter1 = require('./counter');
const counter2 = require('./counter');
counter1.increment();
console.log(counter1.getCount()); // 输出:1
console.log(counter2.getCount()); // 输出:1(同一个模块实例)
说明 :Node.js 会缓存已加载的模块,多次 require 返回同一个实例。
2.2 ES Modules 模块
目标:使用现代 ES6 模块化语法。
步骤 1:启用 ES Modules
在 package.json 中添加:
json
{
"type": "module"
}
或者使用 .mjs 文件扩展名。
步骤 2:导出模块
创建 utils.mjs:
javascript
// 命名导出
export function formatName(first, last) {
return `${first} ${last}`;
}
export function calculateAge(birthYear) {
return new Date().getFullYear() - birthYear;
}
// 默认导出
export default class User {
constructor(name) {
this.name = name;
}
greet() {
return `Hello, ${this.name}!`;
}
}
步骤 3:导入模块
创建 app.mjs:
javascript
// 导入默认导出
import User from './utils.mjs';
// 导入命名导出
import { formatName, calculateAge } from './utils.mjs';
const user = new User('张三');
console.log(user.greet()); // 输出:Hello, 张三!
console.log(formatName('张', '三')); // 输出:张三
console.log(calculateAge(1990)); // 输出:34(根据当前年份)
步骤 4:动态导入
javascript
// 条件加载模块
async function loadModule(condition) {
if (condition) {
const moduleA = await import('./moduleA.mjs');
moduleA.doSomething();
} else {
const moduleB = await import('./moduleB.mjs');
moduleB.doSomething();
}
}
loadModule(true);
2.3 核心模块 vs 第三方模块
目标:理解不同类型模块的导入方式。
javascript
// 核心模块(无需安装,直接使用)
const fs = require('fs');
const path = require('path');
const http = require('http');
// 第三方模块(需 npm install)
const express = require('express');
const lodash = require('lodash');
// 自定义模块(需相对路径)
const utils = require('./utils');
const config = require('./config');
模块解析顺序:
- 核心模块(如
fs、http) node_modules中的第三方模块- 相对路径的自定义模块
实践 3:核心模块使用
3.1 fs 文件系统模块
目标:掌握文件读写、目录操作等文件系统操作。
步骤 1:读取文件
创建 test.txt:
Hello, Node.js!
这是测试文件内容。
同步读取:
javascript
const fs = require('fs');
// 同步读取(阻塞)
const data = fs.readFileSync('./test.txt', 'utf8');
console.log(data);
异步读取(推荐):
javascript
const fs = require('fs').promises;
// 异步读取(非阻塞)
async function readFile() {
try {
const data = await fs.readFile('./test.txt', 'utf8');
console.log(data);
} catch (error) {
console.error('读取文件失败:', error);
}
}
readFile();
步骤 2:写入文件
javascript
const fs = require('fs').promises;
async function writeFile() {
try {
// 写入文件(覆盖)
await fs.writeFile('./output.txt', 'Hello, World!', 'utf8');
console.log('文件写入成功');
// 追加内容
await fs.appendFile('./output.txt', '\n追加的内容');
console.log('内容追加成功');
} catch (error) {
console.error('写入文件失败:', error);
}
}
writeFile();
步骤 3:目录操作
javascript
const fs = require('fs').promises;
const path = require('path');
async function directoryOperations() {
try {
// 创建目录
await fs.mkdir('./uploads', { recursive: true });
console.log('目录创建成功');
// 读取目录内容
const files = await fs.readdir('./');
console.log('目录内容:', files);
// 获取文件信息
const stats = await fs.stat('./test.txt');
console.log('文件大小:', stats.size, '字节');
console.log('创建时间:', stats.birthtime);
console.log('修改时间:', stats.mtime);
// 删除文件
await fs.unlink('./output.txt');
console.log('文件删除成功');
// 删除目录
await fs.rmdir('./uploads');
console.log('目录删除成功');
} catch (error) {
console.error('操作失败:', error);
}
}
directoryOperations();
3.2 path 路径模块
目标:掌握跨平台路径处理,避免路径错误。
javascript
const path = require('path');
// 路径拼接(推荐,跨平台兼容)
const fullPath = path.join('users', 'uploads', 'avatar.jpg');
console.log(fullPath); // Windows: users\uploads\avatar.jpg, Linux: users/uploads/avatar.jpg
// 获取绝对路径
const absolutePath = path.resolve('./test.txt');
console.log(absolutePath); // C:\Users\...\test.txt
// 获取文件名
console.log(path.basename('/users/uploads/avatar.jpg')); // avatar.jpg
// 获取扩展名
console.log(path.extname('avatar.jpg')); // .jpg
// 获取目录名
console.log(path.dirname('/users/uploads/avatar.jpg')); // /users/uploads
// 解析路径
const parsed = path.parse('/users/uploads/avatar.jpg');
console.log(parsed);
// {
// root: '/',
// dir: '/users/uploads',
// base: 'avatar.jpg',
// ext: '.jpg',
// name: 'avatar'
// }
3.3 events 事件模块
目标:理解事件驱动编程,掌握 EventEmitter 使用。
javascript
const EventEmitter = require('events');
// 创建事件发射器
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
// 监听事件
myEmitter.on('greeting', (name) => {
console.log(`Hello, ${name}!`);
});
// 监听一次
myEmitter.once('onceEvent', () => {
console.log('这个事件只会触发一次');
});
// 发射事件
myEmitter.emit('greeting', '张三'); // 输出:Hello, 张三!
myEmitter.emit('onceEvent'); // 输出:这个事件只会触发一次
myEmitter.emit('onceEvent'); // 无输出(已移除监听器)
// 移除监听器
const handler = () => {
console.log('处理函数');
};
myEmitter.on('data', handler);
myEmitter.emit('data'); // 输出:处理函数
myEmitter.removeListener('data', handler);
myEmitter.emit('data'); // 无输出
实战:订单事件系统
javascript
const EventEmitter = require('events');
class OrderSystem extends EventEmitter {
createOrder(orderId) {
console.log(`创建订单: ${orderId}`);
this.emit('orderCreated', orderId);
}
payOrder(orderId) {
console.log(`支付订单: ${orderId}`);
this.emit('orderPaid', orderId);
}
shipOrder(orderId) {
console.log(`发货订单: ${orderId}`);
this.emit('orderShipped', orderId);
}
}
const orderSystem = new OrderSystem();
// 监听订单事件
orderSystem.on('orderCreated', (orderId) => {
console.log(`[通知] 订单 ${orderId} 已创建,等待支付`);
});
orderSystem.on('orderPaid', (orderId) => {
console.log(`[通知] 订单 ${orderId} 已支付,准备发货`);
});
orderSystem.on('orderShipped', (orderId) => {
console.log(`[通知] 订单 ${orderId} 已发货`);
});
// 测试流程
orderSystem.createOrder('ORD001');
orderSystem.payOrder('ORD001');
orderSystem.shipOrder('ORD001');
实践 4:HTTP 服务器
4.1 创建基础 HTTP 服务器
目标:使用 Node.js 内置 http 模块创建 Web 服务器。
步骤 1:最简单的服务器
创建 server.js:
javascript
const http = require('http');
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('Hello, World!');
});
const PORT = 3000;
server.listen(PORT, () => {
console.log(`服务器运行在 http://localhost:${PORT}`);
});
运行:
bash
node server.js
步骤 2:处理不同路由
javascript
const http = require('http');
const server = http.createServer((req, res) => {
const url = req.url;
const method = req.method;
// 设置响应头
res.setHeader('Content-Type', 'application/json; charset=utf-8');
// 路由处理
if (url === '/' && method === 'GET') {
res.statusCode = 200;
res.end(JSON.stringify({ message: '欢迎使用 API' }));
} else if (url === '/users' && method === 'GET') {
res.statusCode = 200;
res.end(JSON.stringify({
users: [
{ id: 1, name: '张三' },
{ id: 2, name: '李四' }
]
}));
} else if (url === '/about' && method === 'GET') {
res.statusCode = 200;
res.end(JSON.stringify({ about: '这是一个 Node.js HTTP 服务器示例' }));
} else {
res.statusCode = 404;
res.end(JSON.stringify({ error: '路由不存在' }));
}
});
const PORT = 3000;
server.listen(PORT, () => {
console.log(`服务器运行在 http://localhost:${PORT}`);
});
测试:
bash
# 测试不同路由
curl http://localhost:3000/
curl http://localhost:3000/users
curl http://localhost:3000/about
curl http://localhost:3000/notfound
4.2 处理 POST 请求
目标:接收并解析 POST 请求体数据。
javascript
const http = require('http');
const server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'application/json; charset=utf-8');
// 处理 POST 请求
if (req.url === '/users' && req.method === 'POST') {
let body = '';
// 监听数据块
req.on('data', (chunk) => {
body += chunk.toString();
});
// 数据接收完成
req.on('end', () => {
try {
const userData = JSON.parse(body);
console.log('接收到用户数据:', userData);
res.statusCode = 201;
res.end(JSON.stringify({
message: '用户创建成功',
user: userData
}));
} catch (error) {
res.statusCode = 400;
res.end(JSON.stringify({ error: '无效的 JSON 数据' }));
}
});
} else {
res.statusCode = 404;
res.end(JSON.stringify({ error: '路由不存在' }));
}
});
const PORT = 3000;
server.listen(PORT, () => {
console.log(`服务器运行在 http://localhost:${PORT}`);
});
测试:
bash
curl -X POST http://localhost:3000/users \
-H "Content-Type: application/json" \
-d '{"name": "张三", "age": 25}'
4.3 静态文件服务器
目标:提供静态文件访问服务。
创建目录结构:
project/
├── server.js
└── public/
├── index.html
└── style.css
创建 public/index.html:
html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>静态文件服务器</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<h1>欢迎使用静态文件服务器</h1>
<p>这是一个 HTML 文件</p>
</body>
</html>
创建 public/style.css:
css
body {
font-family: Arial, sans-serif;
background-color: #f0f0f0;
text-align: center;
padding: 50px;
}
h1 {
color: #333;
}
创建 server.js:
javascript
const http = require('http');
const fs = require('fs').promises;
const path = require('path');
const MIME_TYPES = {
'.html': 'text/html; charset=utf-8',
'.css': 'text/css; charset=utf-8',
'.js': 'application/javascript; charset=utf-8',
'.json': 'application/json; charset=utf-8',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.gif': 'image/gif'
};
const server = http.createServer(async (req, res) => {
// 默认访问 index.html
let filePath = req.url === '/' ? '/index.html' : req.url;
filePath = path.join('./public', filePath);
// 获取文件扩展名
const extname = path.extname(filePath);
const contentType = MIME_TYPES[extname] || 'application/octet-stream';
try {
// 读取文件
const content = await fs.readFile(filePath);
res.statusCode = 200;
res.setHeader('Content-Type', contentType);
res.end(content);
} catch (error) {
if (error.code === 'ENOENT') {
res.statusCode = 404;
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.end('<h1>404 - 文件未找到</h1>');
} else {
res.statusCode = 500;
res.end('服务器内部错误');
}
}
});
const PORT = 3000;
server.listen(PORT, () => {
console.log(`静态文件服务器运行在 http://localhost:${PORT}`);
});
测试:
访问 http://localhost:3000 查看 HTML 页面,CSS 样式会自动加载。
实践 5:项目初始化与配置
5.1 package.json 详解
目标:理解项目配置文件的每个字段含义。
创建项目:
bash
mkdir my-node-app
cd my-node-app
npm init -y
编辑 package.json:
json
{
"name": "my-node-app",
"version": "1.0.0",
"description": "我的第一个 Node.js 应用",
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": ["nodejs", "tutorial"],
"author": "你的名字",
"license": "MIT",
"dependencies": {
"express": "^4.18.2"
},
"devDependencies": {
"nodemon": "^3.0.1"
},
"engines": {
"node": ">=18.0.0"
}
}
字段说明:
| 字段 | 说明 |
|---|---|
name |
项目名称(小写,无空格) |
version |
版本号(语义化版本) |
main |
入口文件 |
scripts |
自定义命令 |
dependencies |
生产环境依赖 |
devDependencies |
开发环境依赖 |
engines |
Node.js 版本要求 |
5.2 环境变量配置
目标:使用环境变量管理配置,区分开发/生产环境。
步骤 1:使用 process.env
创建 config.js:
javascript
const config = {
port: process.env.PORT || 3000,
host: process.env.HOST || 'localhost',
nodeEnv: process.env.NODE_ENV || 'development',
database: {
host: process.env.DB_HOST || '127.0.0.1',
port: process.env.DB_PORT || 5432,
name: process.env.DB_NAME || 'myapp',
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || ''
}
};
module.exports = config;
使用:
javascript
const config = require('./config');
console.log(`运行环境: ${config.nodeEnv}`);
console.log(`服务器端口: ${config.port}`);
console.log(`数据库: ${config.database.host}:${config.database.port}`);
步骤 2:使用 dotenv 管理环境变量
安装:
bash
npm install dotenv
创建 .env 文件:
NODE_ENV=development
PORT=3000
DB_HOST=127.0.0.1
DB_PORT=5432
DB_NAME=myapp
DB_USER=root
DB_PASSWORD=123456
创建 .env.example 文件(提交到 Git):
NODE_ENV=development
PORT=3000
DB_HOST=127.0.0.1
DB_PORT=5432
DB_NAME=myapp
DB_USER=root
DB_PASSWORD=
重要 :将 .env 添加到 .gitignore,不要提交敏感信息!
# .gitignore
node_modules/
.env
使用:
javascript
require('dotenv').config();
const config = {
port: process.env.PORT || 3000,
nodeEnv: process.env.NODE_ENV || 'development',
database: {
host: process.env.DB_HOST,
password: process.env.DB_PASSWORD
}
};
console.log(config);
5.3 nodemon 自动重启
目标:开发时自动重启服务器,提升开发效率。
安装:
bash
npm install -D nodemon
修改 package.json:
json
{
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js"
}
}
运行:
bash
npm run dev
现在修改代码后,服务器会自动重启。
创建 nodemon.json 配置(可选):
json
{
"watch": ["src/"],
"ext": "js,json",
"ignore": ["node_modules/", "test/"],
"delay": "1000"
}
常见问题排查
Q1:node 命令找不到?
原因:Node.js 未正确安装或未添加到 PATH。
解决:
bash
# Windows:重新安装 Node.js,勾选 "Add to PATH"
# 或手动添加 Node.js 安装目录到系统环境变量 PATH
Q2:npm install 速度慢?
解决:使用国内镜像
bash
npm config set registry https://registry.npmmirror.com
Q3:端口被占用?
错误信息 :Error: listen EADDRINUSE: address already in use :::3000
解决:
bash
# Windows 查找占用端口的进程
netstat -ano | findstr :3000
taskkill /PID 进程ID /F
# Linux/macOS
lsof -i :3000
kill -9 进程ID
Q4:require 找不到模块?
错误信息 :Error: Cannot find module 'xxx'
解决:
bash
# 检查是否已安装
npm list xxx
# 重新安装
npm install xxx
# 清除缓存
npm cache clean --force
npm install
Q5:ES Modules 语法报错?
错误信息 :SyntaxError: Cannot use import statement outside a module
解决:
在 package.json 中添加:
json
{
"type": "module"
}
或使用 .mjs 扩展名。
学习建议
1. 先掌握 CommonJS :大多数现有项目使用 CommonJS,先熟练使用再学 ES Modules
2. 理解异步编程 :Node.js 的核心是异步,务必理解回调、Promise、async/await
3. 动手实践 :不要只看文档,一定要动手写代码
4. 查看官方文档 :遇到问题优先查阅官方文档:https://nodejs.org/docs
5. 使用版本控制:项目代码使用 Git 管理,方便回滚和协作