Appearance
Node.js 模块系统
Node.js使用模块化的方式来组织代码,使得代码更易于维护和重用。本章详细介绍Node.js的模块系统。
CommonJS模块系统
Node.js使用CommonJS规范来实现模块化,这是Node.js原生的模块系统。
模块导出
导出单个函数或变量
javascript
// math.js
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
// 导出单个函数
module.exports = add;
// 或者导出多个函数
module.exports = {
add,
multiply
};
导出为属性
javascript
// math.js
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
// 分别导出
module.exports.add = add;
module.exports.subtract = subtract;
// 或使用ES6简写
module.exports = { add, subtract };
导出构造函数或类
javascript
// user.js
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
getInfo() {
return `${this.name} (${this.email})`;
}
}
module.exports = User;
模块导入
javascript
// app.js
// 导入整个模块
const math = require('./math');
console.log(math.add(2, 3)); // 5
// 解构导入
const { add, multiply } = require('./math');
console.log(add(2, 3)); // 5
// 导入类
const User = require('./user');
const user = new User('张三', 'zhang@example.com');
console.log(user.getInfo()); // 张三 (zhang@example.com)
核心模块
Node.js提供了一系列内置的核心模块:
javascript
// 文件系统模块
const fs = require('fs');
// HTTP模块
const http = require('http');
// 路径模块
const path = require('path');
// 操作系统模块
const os = require('os');
// 加密模块
const crypto = require('crypto');
// URL模块
const url = require('url');
常用核心模块示例
文件系统操作
javascript
const fs = require('fs');
const path = require('path');
// 读取文件(异步)
fs.readFile('./data.txt', 'utf8', (err, data) => {
if (err) {
console.error('读取文件失败:', err);
return;
}
console.log('文件内容:', data);
});
// 读取文件(同步)
try {
const data = fs.readFileSync('./data.txt', 'utf8');
console.log('文件内容:', data);
} catch (err) {
console.error('读取文件失败:', err);
}
// 写入文件
fs.writeFile('./output.txt', 'Hello, Node.js!', (err) => {
if (err) {
console.error('写入文件失败:', err);
return;
}
console.log('文件写入成功');
});
路径处理
javascript
const path = require('path');
// 路径拼接
const filePath = path.join(__dirname, 'data', 'file.txt');
console.log(filePath); // /current/directory/data/file.txt
// 获取路径信息
const parsedPath = path.parse('/home/user/documents/file.txt');
console.log(parsedPath);
// {
// root: '/',
// dir: '/home/user/documents',
// base: 'file.txt',
// ext: '.txt',
// name: 'file'
// }
// 获取绝对路径
const absolutePath = path.resolve('data', 'file.txt');
console.log(absolutePath); // /current/directory/data/file.txt
第三方模块
npm模块安装和使用
bash
# 安装生产依赖
npm install express
# 安装开发依赖
npm install --save-dev nodemon
# 安装特定版本
npm install lodash@4.17.20
# 安装全局模块
npm install -g pm2
使用第三方模块
javascript
// 安装express后
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Hello, Express!');
});
app.listen(3000, () => {
console.log('服务器运行在端口3000');
});
ES6模块系统
Node.js也支持ES6模块语法(需要在package.json中设置"type": "module"或使用.mjs扩展名):
ES6模块导出
javascript
// math.mjs 或在package.json设置"type": "module"
// 默认导出
export default function add(a, b) {
return a + b;
}
// 命名导出
export function multiply(a, b) {
return a * b;
}
export const PI = 3.14159;
// 或者在末尾导出
function subtract(a, b) {
return a - b;
}
export { subtract };
ES6模块导入
javascript
// app.mjs
import add, { multiply, PI } from './math.mjs';
import { subtract } from './math.mjs';
console.log(add(2, 3)); // 5
console.log(multiply(2, 3)); // 6
console.log(PI); // 3.14159
console.log(subtract(5, 3)); // 2
// 导入所有内容
import * as math from './math.mjs';
console.log(math.add(2, 3)); // 5
模块缓存机制
Node.js会缓存模块,确保模块只执行一次:
javascript
// counter.js
let count = 0;
function increment() {
return ++count;
}
console.log('模块执行');
module.exports = { increment };
javascript
// app.js
const counter1 = require('./counter');
const counter2 = require('./counter');
console.log(counter1.increment()); // 1
console.log(counter2.increment()); // 2 (共享同一个模块实例)
// "模块执行" 只会输出一次,因为模块被缓存了
模块路径解析
Node.js按照以下顺序解析模块路径:
- 核心模块
- 相对路径(以./或../开头)
- 绝对路径(以/开头)
- node_modules中的模块
javascript
// 相对路径导入
const myModule = require('./myModule');
const siblingModule = require('../sibling/Module');
// node_modules中的模块
const express = require('express');
const lodash = require('lodash');
// 模块路径解析示例
// require('myModule') 会按以下顺序查找:
// 1. ./node_modules/myModule
// 2. ../node_modules/myModule
// 3. ../../node_modules/myModule
// 直到根目录
package.json和package-lock.json
package.json
json
{
"name": "my-node-app",
"version": "1.0.0",
"description": "A sample Node.js application",
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js",
"test": "jest"
},
"dependencies": {
"express": "^4.18.0",
"lodash": "^4.17.21"
},
"devDependencies": {
"nodemon": "^2.0.0",
"jest": "^28.0.0"
},
"engines": {
"node": ">=14.0.0"
}
}
模块最佳实践
javascript
// 1. 使用解构导入减少重复
const { readFile, writeFile } = require('fs');
// 2. 按类型分组导入
// 核心模块
const fs = require('fs');
const path = require('path');
// 第三方模块
const express = require('express');
const _ = require('lodash');
// 本地模块
const config = require('./config');
const utils = require('./utils');
// 3. 使用index.js作为目录入口
// ./lib/index.js
module.exports = {
validator: require('./validator'),
formatter: require('./formatter'),
parser: require('./parser')
};
// 可以这样导入
const lib = require('./lib'); // 自动加载index.js
自定义模块示例
创建一个实用工具模块:
javascript
// utils.js
const fs = require('fs');
const path = require('path');
// 文件操作工具
const fileUtils = {
// 检查文件是否存在
exists: (filePath) => {
return fs.existsSync(filePath);
},
// 读取JSON文件
readJSON: (filePath) => {
try {
const data = fs.readFileSync(filePath, 'utf8');
return JSON.parse(data);
} catch (err) {
throw new Error(`读取JSON文件失败: ${err.message}`);
}
},
// 写入JSON文件
writeJSON: (filePath, data) => {
try {
const json = JSON.stringify(data, null, 2);
fs.writeFileSync(filePath, json, 'utf8');
} catch (err) {
throw new Error(`写入JSON文件失败: ${err.message}`);
}
}
};
// 数据验证工具
const validationUtils = {
isEmail: (email) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
},
isURL: (url) => {
try {
new URL(url);
return true;
} catch {
return false;
}
}
};
module.exports = {
fileUtils,
validationUtils
};
模块系统是Node.js的重要特性,合理使用模块可以让代码更清晰、更易于维护。