Node.js开发指南:内置模块、HTTP服务与Nodemon工具
Node.js作为一款基于Chrome V8引擎的JavaScript运行时,广泛应用于服务器端开发。本文将深入探讨Node.js的核心特性,包括模块化机制、命令行操作、常用的内置模块,以及如何利用Express框架构建Web服务,并介绍强大的开发辅助工具Nodemon。
1. Node.js的模块系统
Node.js默认采用CommonJS规范实现模块化。在这种规范下,每个JavaScript文件都被视为一个独立的模块。模块之间通过特定的语法进行导入和导出。
导出模块内容:
可以使用module.exports或exports对象来暴露模块的接口。module.exports通常用于导出一个默认对象或类,而exports则用于导出多个命名成员。
// utils/mathOperations.js
const add = (a, b) => a + b;
const subtract = (a, b) => a - b;
// 方式一:通过 module.exports 导出一个对象
// module.exports = {
// addFunction: add,
// subtractFunction: subtract
// };
// 方式二:通过 exports 导出多个命名成员
exports.add = add;
exports.subtract = subtract;
exports.multiply = (a, b) => a * b;
导入模块:
使用require()函数可以加载并使用其他模块导出的功能。文件路径通常是相对路径,且.js扩展名可以省略。
// main.js
const math = require('./utils/mathOperations'); // .js 后缀可省略
console.log('2 + 3 =', math.add(2, 3)); // 输出: 2 + 3 = 5
console.log('5 - 1 =', math.subtract(5, 1)); // 输出: 5 - 1 = 4
console.log('4 * 6 =', math.multiply(4, 6)); // 输出: 4 * 6 = 24
// 对比ES6模块化语法:
// import { add, subtract } from './utils/mathOperations.js';
// console.log(add(2, 3));
2. 执行JavaScript文件与常用命令行指令
要在Node.js环境中执行JavaScript文件,只需在终端中使用node命令后跟文件名。例如:node app.js。
常用的命令行操作:
cd [目录路径]:切换当前工作目录。使用Tab键可自动补全路径。cd ..:返回上一级目录。[盘符]::在Windows系统中切换磁盘(如D:)。ls或dir:列出当前目录下的文件和子目录。mkdir [目录名]:创建新目录。rmdir [目录名]:删除空目录。cls或clear:清空终端屏幕。ipconfig(Windows) /ifconfig(Linux/macOS):显示网络配置信息,包括IP地址。systeminfo(Windows) /uname -a(Linux/macOS):查看操作系统详细信息。
3. Node.js内置核心模块
Node.js提供了一系列强大的内置模块,无需安装即可直接使用,它们提供了操作系统、文件系统、网络通信等底层功能。
3.1 文件系统模块 (fs)
fs模块提供了与文件和目录交互的丰富API。
const fs = require('fs');
const path = require('path');
// 异步读取文件内容
fs.readFile(path.join(__dirname, 'config.txt'), 'utf8', (err, data) => {
if (err) {
console.error('读取文件失败:', err.message);
return;
}
console.log('文件内容 (异步):', data);
});
// 同步读取文件内容
try {
const syncData = fs.readFileSync(path.join(__dirname, 'config.txt'), 'utf8');
console.log('文件内容 (同步):', syncData);
} catch (error) {
console.error('同步读取文件失败:', error.message);
}
// 异步写入文件内容 (如果文件不存在则创建,存在则覆盖)
fs.writeFile('log.txt', '这是一条日志信息\n', { encoding: 'utf8', flag: 'a' }, (err) => { // flag: 'a' 表示追加
if (err) {
console.error('写入文件失败:', err.message);
return;
}
console.log('日志写入成功!');
});
// 检查文件或目录是否存在
const filePath = 'log.txt';
if (fs.existsSync(filePath)) {
console.log(`${filePath} 存在.`);
} else {
console.log(`${filePath} 不存在.`);
}
// 获取文件/目录信息 (异步)
fs.stat(filePath, (err, stats) => {
if (err) {
console.error('获取文件信息失败:', err.message);
return;
}
console.log(`${filePath} 是文件吗?`, stats.isFile());
console.log(`${filePath} 是目录吗?`, stats.isDirectory());
console.log(`${filePath} 大小 (字节):`, stats.size);
});
// 删除文件 (异步)
// fs.unlink('temp.txt', (err) => {
// if (err) console.error('删除文件失败:', err.message);
// else console.log('temp.txt 已删除.');
// });
3.2 路径处理模块 (path)
path模块用于处理文件和目录路径,兼容不同操作系统的路径分隔符。
const path = require('path');
// 路径拼接:将多个路径段合并成一个规范化的路径
console.log('路径合并:', path.join('/users', 'john', 'documents', 'report.pdf')); // 输出: /users/john/documents/report.pdf
// 解析为绝对路径:将一系列路径段或相对路径解析为绝对路径
console.log('解析绝对路径:', path.resolve('data', '../config', 'app.json')); // 输出: /your/current/working/dir/config/app.json (根据当前工作目录变化)
// __filename: 当前模块文件的绝对路径
console.log('当前文件路径:', __filename);
// __dirname: 当前模块文件所在目录的绝对路径
console.log('当前目录路径:', __dirname);
// 获取路径中的文件名
console.log('文件名:', path.basename('/home/user/app/index.js')); // 输出: index.js
console.log('文件名 (无扩展名):', path.basename('/home/user/app/index.js', '.js')); // 输出: index
// 获取路径中的目录名
console.log('目录名:', path.dirname('/home/user/app/index.js')); // 输出: /home/user/app
// 获取路径中的文件扩展名
console.log('扩展名:', path.extname('document.docx')); // 输出: .docx
console.log('扩展名:', path.extname('archive.tar.gz')); // 输出: .gz
3.3 操作系统信息模块 (os)
os模块提供了一系列与操作系统相关的方法和属性。
const os = require('os');
console.log('操作系统类型:', os.type()); // 例如: 'Linux', 'Darwin' (macOS), 'Windows_NT'
console.log('操作系统平台:', os.platform()); // 例如: 'linux', 'darwin', 'win32'
console.log('CPU信息:', os.cpus().length, '核');
console.log('总内存 (MB):', (os.totalmem() / 1024 / 1024).toFixed(2));
console.log('空闲内存 (MB):', (os.freemem() / 1024 / 1024).toFixed(2));
console.log('主机名:', os.hostname());
console.log('默认换行符:', JSON.stringify(os.EOL)); // 在Windows上是"\r\n",在Linux/macOS上是"\n"
3.4 URL解析模块 (url)
url模块用于解析和格式化URL字符串。
const url = require('url');
const myUrl = 'http://user:pass@host.com:8080/p/a/t/h?query=string#hash';
const parsedUrl = url.parse(myUrl, true); // true表示解析query字符串为对象
console.log('协议:', parsedUrl.protocol); // http:
console.log('主机名:', parsedUrl.hostname); // host.com
console.log('端口:', parsedUrl.port); // 8080
console.log('路径:', parsedUrl.pathname); // /p/a/t/h
console.log('查询参数:', parsedUrl.query); // { query: 'string' }
console.log('哈希:', parsedUrl.hash); // #hash
3.5 查询字符串模块 (querystring)
querystring模块用于解析和格式化URL查询字符串,类似于HTTP GET请求中的参数部分。
const querystring = require('querystring');
// 将查询字符串解析为对象
const queryStr = 'name=Alice&age=30&city=New%20York';
const parsedObj = querystring.parse(queryStr);
console.log('解析后的对象:', parsedObj); // { name: 'Alice', age: '30', city: 'New York' }
// 将对象格式化为查询字符串
const objToFormat = { product: 'laptop', price: '1200', inStock: 'true' };
const formattedStr = querystring.stringify(objToFormat);
console.log('格式化后的字符串:', formattedStr); // product=laptop&price=1200&inStock=true
// querystring.decode 和 querystring.encode 分别是 querystring.parse 和 querystring.stringify 的别名
3.6 HTTP模块 (http)
http模块是Node.js构建Web服务器的核心,允许创建HTTP服务器和客户端。
创建基本HTTP服务器
// server.js
const http = require('http');
const webServer = http.createServer((request, response) => {
// request 对象包含客户端请求的所有信息
// response 对象用于向客户端发送响应
console.log(`收到来自 ${request.socket.remoteAddress} 的请求: ${request.method} ${request.url}`);
// 设置响应头,解决中文乱码问题
response.setHeader('Content-Type', 'text/html; charset=utf-8');
// 根据请求URL返回不同内容
if (request.url === '/') {
response.statusCode = 200; // 设置状态码为200 (OK)
response.end('<h1>欢迎访问我的Node.js服务器!</h1><p>这是主页内容。</p>');
} else if (request.url === '/about') {
response.statusCode = 200;
response.end('<h2>关于我们</h2><p>Node.js是一款强大的运行时。</p>');
} else {
response.statusCode = 404; // 设置状态码为404 (Not Found)
response.end('<h1>404 Not Found</h1><p>您请求的页面不存在。</p>');
}
});
const PORT = 3000;
webServer.listen(PORT, () => {
console.log(`HTTP服务器已启动,监听端口 ${PORT}`);
console.log(`请访问: http://localhost:${PORT}`);
});
提供静态资源服务
以下示例展示如何搭建一个简单的静态文件服务器,将public目录下的文件作为静态资源提供。
// staticServer.js
const http = require('http');
const path = require('path');
const fs = require('fs');
// 定义静态文件目录
const STATIC_DIR = path.join(__dirname, 'public');
const staticWebServer = http.createServer((req, res) => {
let requestPath = req.url;
// 处理根路径请求,默认返回 index.html
if (requestPath === '/') {
requestPath = '/index.html';
}
// 忽略 favicon.ico 请求,或者提供一个真实的 favicon
if (requestPath === '/favicon.ico') {
res.statusCode = 204; // No Content
res.end();
return;
}
const filePath = path.join(STATIC_DIR, requestPath);
// 检查文件是否存在
fs.access(filePath, fs.constants.F_OK, (err) => {
if (err) {
// 文件不存在
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.statusCode = 404;
res.end('<h1>404 资源未找到</h1><p>您请求的文件或页面不存在。</p>');
return;
}
// 文件存在,读取并返回
fs.readFile(filePath, (readErr, data) => {
if (readErr) {
// 读取文件失败
console.error(`读取文件失败: ${readErr.message}`);
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.statusCode = 500;
res.end('<h1>500 服务器内部错误</h1><p>无法提供请求的资源。</p>');
return;
}
// 根据文件扩展名设置MIME类型
const ext = path.extname(filePath);
let contentType = 'application/octet-stream';
switch (ext) {
case '.html': contentType = 'text/html'; break;
case '.css': contentType = 'text/css'; break;
case '.js': contentType = 'application/javascript'; break;
case '.json': contentType = 'application/json'; break;
case '.png': contentType = 'image/png'; break;
case '.jpg':
case '.jpeg': contentType = 'image/jpeg'; break;
// 更多类型...
}
res.setHeader('Content-Type', `${contentType}; charset=utf-8`);
res.statusCode = 200;
res.end(data);
});
});
});
const STATIC_PORT = 8080;
staticWebServer.listen(STATIC_PORT, () => {
console.log(`静态文件服务器在 http://localhost:${STATIC_PORT} 运行`);
});
在项目根目录创建一个public文件夹,并在其中放置index.html、style.css等文件进行测试。
处理GET请求参数
const http = require('http');
const url = require('url');
http.createServer((req, res) => {
res.setHeader('Content-Type', 'application/json; charset=utf-8');
const { pathname, query } = url.parse(req.url, true); // true 将查询字符串解析为对象
if (pathname === '/api/data') {
res.statusCode = 200;
res.end(JSON.stringify({
message: '接收到GET请求数据',
parameters: query
}));
} else {
res.statusCode = 404;
res.end(JSON.stringify({ error: '路径不存在' }));
}
}).listen(3001, () => console.log('GET API Server running on http://localhost:3001'));
处理POST请求体数据
POST请求的数据可能分批发送,需要监听data事件来收集数据块,并在end事件触发时将它们拼接起来。
const http = require('http');
const querystring = require('querystring');
http.createServer((req, res) => {
if (req.method === 'POST' && req.url === '/api/submit') {
let requestBodyChunks = [];
req.on('data', (chunk) => {
requestBodyChunks.push(chunk);
});
req.on('end', () => {
const fullRequestBody = Buffer.concat(requestBodyChunks).toString();
const contentType = req.headers['content-type'] || '';
let parsedData = {};
if (contentType.includes('application/x-www-form-urlencoded')) {
parsedData = querystring.parse(fullRequestBody);
} else if (contentType.includes('application/json')) {
try {
parsedData = JSON.parse(fullRequestBody);
} catch (e) {
res.statusCode = 400;
res.end(JSON.stringify({ error: '无效的JSON数据' }));
return;
}
}
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.statusCode = 200;
res.end(JSON.stringify({
message: '成功接收POST请求数据',
receivedData: parsedData
}));
});
} else {
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.statusCode = 405;
res.end(JSON.stringify({ error: '不支持的请求方法或路径' }));
}
}).listen(3002, () => console.log('POST API Server running on http://localhost:3002'));
HTTP代理示例
使用http-proxy-middleware可以方便地创建HTTP代理,常用于解决开发环境的跨域问题。
const http = require('http');
// 首先需要安装:npm install http-proxy-middleware
const { createProxyMiddleware } = require('http-proxy-middleware');
// 示例代理配置
const proxyConfig = createProxyMiddleware('/api', {
target: 'https://jsonplaceholder.typicode.com', // 目标API服务地址
changeOrigin: true, // 改变请求头中的Host字段,使其与目标URL的主机名相同
pathRewrite: {
'^/api': '', // 将请求路径中的 /api 替换为空,例如 /api/users 会被代理到 /users
},
onProxyReq: (proxyReq, req, res) => {
// 可以在代理请求发送前修改请求
console.log(`代理请求到: ${proxyReq.path}`);
},
onError: (err, req, res) => {
res.writeHead(500, {
'Content-Type': 'text/plain',
});
res.end('代理请求失败: ' + err.message);
}
});
const proxyServer = http.createServer((req, res) => {
// 只有请求路径匹配 /api 才会被代理
if (req.url.startsWith('/api')) {
proxyConfig(req, res); // 调用代理中间件
} else {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('欢迎访问我的主页,尝试访问 /api/posts');
}
});
proxyServer.listen(3003, () => {
console.log('代理服务器运行在 http://localhost:3003');
console.log('访问 http://localhost:3003/api/posts 即可代理到 https://jsonplaceholder.typicode.com/posts');
});
4. Express.js框架入门
Express是Node.js平台上一个快速、开放、极简的Web开发框架。它简化了Web服务器和API接口的构建过程。
安装依赖:
npm install express
npm install art-template express-art-template # 如需使用模板引擎
基本Express应用与模板引擎
以下示例展示如何使用Express创建一个简单的Web服务器,并结合art-template渲染动态页面。
// expressApp.js
const express = require('express');
const path = require('path');
// 创建Express应用实例
const app = express();
// 配置静态资源目录
// 任何以 /static 开头的请求都会去 'public' 目录下查找文件
// 例如:访问 http://localhost:4000/static/style.css 会读取 public/style.css
app.use('/static', express.static(path.join(__dirname, 'public')));
// 配置模板引擎 (以 .html 结尾的文件使用 art-template 渲染)
app.engine('html', require('express-art-template'));
// 设置模板文件存放目录
app.set('views', path.join(__dirname, 'views'));
// 设置模板引擎的默认后缀 (可选,如果文件名带有后缀可不设置)
app.set('view engine', 'html');
// 定义根路由,渲染 'index.html' 模板
app.get('/', (req, res) => {
const pageData = {
title: 'Express应用首页',
features: ['快速开发', '强大的路由', '中间件支持'],
isLoggedIn: true
};
// 渲染 views/index.html 模板,并传入数据
res.render('index', pageData);
});
// 定义一个简单的API路由
app.get('/api/greeting', (req, res) => {
const name = req.query.name || '访客';
res.json({ message: `你好,${name}!` }); // 发送JSON响应
});
// 监听指定端口
const EXPRESS_PORT = 4000;
app.listen(EXPRESS_PORT, () => {
console.log(`Express应用运行在 http://localhost:${EXPRESS_PORT}`);
});
在项目根目录创建public文件夹(存放CSS/JS等静态文件)和views文件夹(存放HTML模板文件)。
views/index.html示例:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>{{ title }}</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<h1>{{ title }}</h1>
<p>当前用户:{{ isLoggedIn ? '已登录' : '未登录' }}</p>
<h2>主要特性:</h2>
<ul>
{{ each features }}
<li>{{ $value }}</li>
{{ /each }}
</ul>
<script src="/static/app.js"></script>
</body>
</html>
Express路由基础
Express的核心是其强大的路由系统,允许根据HTTP方法和URL路径定义不同的请求处理逻辑。
// expressRouter.js
const express = require('express');
const app = express();
// 中间件:用于解析JSON格式的请求体
app.use(express.json());
// 中间件:用于解析URL-encoded格式的请求体 (例如HTML表单提交)
app.use(express.urlencoded({ extended: true }));
// GET请求示例
app.get('/users/:id', (req, res) => {
const userId = req.params.id; // 获取URL参数
const queryParam = req.query.sort; // 获取查询字符串参数
res.status(200).send(`查询用户 ID: ${userId}, 排序方式: ${queryParam || '默认'}`);
});
// POST请求示例
app.post('/products', (req, res) => {
const productData = req.body; // 获取请求体数据
if (!productData || !productData.name || !productData.price) {
return res.status(400).json({ error: '产品名称和价格是必填项' });
}
console.log('接收到新产品数据:', productData);
res.status(201).json({ message: '产品创建成功', data: productData });
});
// PUT请求示例
app.put('/items/:itemId', (req, res) => {
const itemId = req.params.itemId;
const updateData = req.body;
res.status(200).json({ message: `项目 ${itemId} 已更新`, new_data: updateData });
});
// DELETE请求示例
app.delete('/data/:recordId', (req, res) => {
const recordId = req.params.recordId;
res.status(204).send(`记录 ${recordId} 已删除`); // 204 No Content
});
app.listen(4001, () => {
console.log('Express路由服务器运行在 http://localhost:4001');
});
5. Nodemon开发辅助工具
nodemon是一个非常实用的命令行工具,它会监听Node.js项目中的文件变动。一旦检测到文件更改,nodemon会自动重启Node.js应用程序,极大地提高了开发效率,避免了手动停止和重启服务器的繁琐操作。
安装 Nodemon:
nodemon通常作为全局工具安装,以便在任何项目中使用。
npm install --global nodemon
# 或者使用 yarn
yarn global add nodemon
验证安装:
nodemon --version
使用 Nodemon 运行文件:
安装后,可以使用nodemon命令替代node命令来启动您的JavaScript文件。
nodemon your-app.js
现在,当your-app.js文件或其依赖的文件发生修改并保存时,Node.js应用程序将自动重新启动。
卸载 Nodemon (如果不再需要):
npm uninstall --global nodemon
# 或者使用 yarn
yarn global remove nodemon