📚 Node.js 模块化学习笔记
📑 目录
- 学习目标
- 一、模块化简介
- [1.1 什么是模块化](#1.1 什么是模块化)
- [1.2 CommonJS 标准语法](#1.2 CommonJS 标准语法)
- [二、ECMAScript 标准语法](#二、ECMAScript 标准语法)
- [2.1 默认导出与导入](#2.1 默认导出与导入)
- [2.2 命名导出与导入](#2.2 命名导出与导入)
- [2.3 两种标准如何选择](#2.3 两种标准如何选择)
- 三、包的概念
- [四、npm 软件包管理器](#四、npm 软件包管理器)
- [4.1 npm 基础使用](#4.1 npm 基础使用)
- [4.2 安装所有依赖](#4.2 安装所有依赖)
- [4.3 全局软件包](#4.3 全局软件包)
- 五、知识总结
- 六、常用命令速查
学习目标
- ✅ 了解 Node.js 模块化的概念和好处
- ✅ 了解 Node.js 中软件包的概念和引入
- ✅ 掌握 Node.js 的 npm 软件包管理器使用
- ✅ 了解 Node.js 中本地软件包与全局软件包区别
一、模块化简介
1.1 什么是模块化
💡 一句话理解:把代码拆分成一个个独立的文件(模块),每个模块有自己的作用域,可以按需加载和复用。
🎯 模块化的好处
| 好处 | 说明 |
|---|---|
| 独立作用域 | 每个文件都是独立的,变量不会互相污染 |
| 代码复用 | 写好的模块可以在多个地方使用 |
| 按需加载 | 需要哪个模块就加载哪个 |
| 易于维护 | 代码结构清晰,方便管理 |
💭 思考:这和前端 JS 直接写在 HTML 里完全不一样!以前所有变量都在全局作用域,很容易冲突。模块化让代码更有组织性,就像把工具分类放在不同的抽屉里。
🔒 模块的封装机制
Node.js 在执行模块代码时,会用函数封装器包裹起来:
javascript
(function(exports, require, module, __filename, __dirname) {
// 你的模块代码在这里
});
📝 笔记:这就是为什么模块内的变量默认是私有的,不会污染全局!
1.2 CommonJS 标准语法
🎯 Node.js 默认的模块化标准
📤 导出语法
javascript
// utils.js
const baseURL = 'http://hmajax.jdfs.net';
const getArraySum = arr => arr.reduce((sum, item) => sum += item, 0);
// 导出
module.exports = {
url: baseURL, // 对外暴露叫 url
arraySum: getArraySum // 对外暴露叫 arraySum
};
📥 导入语法
javascript
// index.js
const obj = require('./utils.js');
console.log(obj); // { url: '...', arraySum: [Function] }
const result = obj.arraySum([5, 1, 2, 3]);
console.log(result); // 11
⚠️ 路径选择规则
| 模块类型 | 写法 | 示例 |
|---|---|---|
| 内置模块 | 直接写模块名 | require('fs') |
| 自定义模块 | 写相对路径 | require('./utils.js') |
| 第三方模块 | 直接写包名 | require('lodash') |
💭 踩坑记录 :自定义模块一定要加
./或../,否则 Node.js 会认为是内置模块或 node_modules 里的包!我之前忘记加./报错了好几次 😅
二、ECMAScript 标准语法
💡 ES6 推出的模块化标准,前端工程化项目常用
2.1 默认导出与导入
📤 默认导出
javascript
// utils.js
const baseURL = 'http://hmajax.jdfs.net';
const getArraySum = arr => arr.reduce((sum, item) => sum += item, 0);
// 默认导出(一个模块只能有一个默认导出)
export default {
url: baseURL,
arraySum: getArraySum
};
📥 默认导入
javascript
// index.js
import obj from './utils.js';
console.log(obj);
const result = obj.arraySum([10, 20, 30]);
console.log(result); // 60
⚠️ 重要:启用 ES Module
Node.js 默认使用 CommonJS,要使用 ES Module 需要在 package.json 中设置:
json
{
"type": "module"
}
📝 笔记 :设置了
"type": "module"后,整个项目就都使用 ES Module 标准了。这时候就不能用require了,要用import!
2.2 命名导出与导入
📤 命名导出
javascript
// utils.js
// 直接在定义时导出
export const baseURL = 'http://hmajax.jdfs.net';
export const getArraySum = arr => arr.reduce((sum, item) => sum += item, 0);
📥 命名导入
javascript
// index.js
// 使用花括号,变量名必须和导出时一致
import { baseURL, getArraySum } from './utils.js';
console.log(baseURL); // http://hmajax.jdfs.net
console.log(getArraySum); // [Function]
const result = getArraySum([10, 21, 33]);
console.log(result); // 64
🎯 命名 vs 默认导出对比
| 特性 | 默认导出 export default |
命名导出 export |
|---|---|---|
| 导出数量 | 一个模块只能有一个 | 可以有多个 |
| 导入方式 | import xxx from '...' |
import { xxx } from '...' |
| 导入时命名 | 可以自定义名字 | 必须和导出时同名 |
| 适用场景 | 整个模块作为一个整体导出 | 按需导出多个功能 |
💭 思考 :命名导出更灵活,可以按需引入。比如 lodash 就是用命名导出,我只引入
debounce和throttle,不会把整个库都打包进来。
2.3 两种标准如何选择
📊 CommonJS vs ECMAScript 对比
| 对比项 | CommonJS | ECMAScript (ES6) |
|---|---|---|
| 语法 | require / module.exports |
import / export |
| 加载方式 | 运行时同步加载 | 编译时静态分析 |
| Node.js 支持 | ✅ 原生支持 | 需设置 "type": "module" |
| 浏览器支持 | ❌ 不支持 | ✅ 现代浏览器支持 |
| 使用场景 | Node.js 后端项目 | 前端工程化项目 |
| Tree Shaking | ❌ 不支持 | ✅ 支持 |
🎯 选择建议
💡 我的理解:
- 纯 Node.js 后端项目 → CommonJS(简单直接)
- 前端工程化项目(Webpack/Vite)→ ECMAScript(更好的 Tree Shaking)
- 现代全栈项目 → ECMAScript(前后端统一标准)
三、包的概念
📦 什么是包
💡 定义 :将模块、代码、其他资料整合成一个文件夹,这个文件夹就叫包。
🏷️ 包的分类
| 类型 | 说明 | 示例 |
|---|---|---|
| 项目包 | 编写项目和业务逻辑的文件夹 | 我的网站项目 |
| 软件包 | 封装工具和方法供他人使用 | lodash、dayjs |
📋 包的要求
包文件夹必须 包含 package.json 文件,用于记录包的清单信息:
json
{
"name": "my-utils",
"version": "1.0.0",
"main": "index.js", // ⭐ 入口文件
"description": "工具函数包"
}
🔍 包的引入规则
引入一个包文件夹时,Node.js 会按以下顺序查找:
- 找包文件夹下的
index.js - 如果没有
index.js,找package.json中main字段指定的文件
🎯 实践:创建自己的工具包
utils/ # 包文件夹
├── package.json # 包清单
├── index.js # 统一出口 ⭐
└── lib/ # 工具模块文件夹
├── arr.js # 数组工具
└── str.js # 字符串工具
utils/lib/arr.js:
javascript
const getArraySum = arr => arr.reduce((sum, item) => sum += item, 0);
module.exports = { getArraySum };
utils/lib/str.js:
javascript
const checkUser = name => name.length >= 2;
const checkPwd = pwd => pwd.length >= 6;
module.exports = { checkUser, checkPwd };
utils/index.js(统一出口):
javascript
/**
* 本文件是 utils 工具包的唯一出口
* 作用:把所有工具模块方法集中起来,统一向外暴露
*/
const { getArraySum } = require('./lib/arr.js');
const { checkUser, checkPwd } = require('./lib/str.js');
// 统一导出所有函数
module.exports = {
getArraySum,
checkUser,
checkPwd
};
使用包:
javascript
// index.js
const obj = require('./utils'); // ⭐ 注意:导入的是文件夹!
console.log(obj);
const result = obj.getArraySum([10, 20, 30]);
console.log(result); // 60
💭 思考 :统一出口的设计模式很实用!用户只需要
require('./utils'),就能拿到所有工具函数,不用关心内部结构。这也是 npm 包的常见设计。
四、npm 软件包管理器
🎯 npm (Node Package Manager):Node.js 官方的软件包管理器,用于下载和管理依赖。
4.1 npm 基础使用
📋 使用步骤
bash
# 1. 初始化项目(生成 package.json)
npm init -y
# 2. 下载软件包
npm i 软件包名称
# 3. 在代码中使用
📝 package.json 详解
json
{
"name": "my-project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": { // ⭐ 生产依赖
"dayjs": "^1.11.10"
},
"devDependencies": {} // 开发依赖
}
🎯 实践:使用 dayjs 格式化日期
bash
# 1. 初始化
npm init -y
# 2. 安装 dayjs
npm i dayjs
javascript
// index.js
const dayjs = require('dayjs');
const now = dayjs();
console.log(now.format('YYYY-MM-DD HH:mm:ss'));
📁 安装后的文件结构
my-project/
├── package.json # 依赖清单
├── package-lock.json # 锁定版本
├── index.js # 项目代码
└── node_modules/ # ⭐ 下载的软件包
└── dayjs/
💭 注意 :
node_modules文件夹通常很大,不要 提交到 Git!要在.gitignore中忽略它。
4.2 安装所有依赖
🤔 常见问题
Q:拿到了别人的项目,没有 node_modules,能运行吗?
❌ 不能! 因为缺少项目依赖的软件包源码。
Q:为什么不直接传递 node_modules?
✅ 原因:
- 文件夹太大,传输慢
- npm 有本地缓存,重新下载更快
- 不同系统可能有兼容性问题
🛠️ 解决方案
bash
# 在项目目录下运行
npm i
# 或
npm install
这个命令会根据 package.json 中的 dependencies,自动下载所有依赖到 node_modules。
📝 笔记 :这是协作开发的标配流程!克隆项目 →
npm i→ 运行代码。
4.3 全局软件包
📊 本地包 vs 全局包对比
| 类型 | 作用范围 | 存放位置 | 用途 | 安装方式 |
|---|---|---|---|---|
| 本地软件包 | 当前项目 | node_modules |
封装属性和方法 | npm i 包名 |
| 全局软件包 | 本机所有项目 | 系统目录 | 封装命令和工具 | npm i 包名 -g |
🎯 常用全局包:nodemon
作用 :替代
node命令,检测代码更改后自动重启程序。
bash
# 全局安装
npm i nodemon -g
# 使用(代替 node 命令)
nodemon index.js
💡 效果演示
bash
$ nodemon index.js
[nodemon] starting `node index.js`
Server running on port 3000
# 当你修改代码并保存...
[nodemon] restarting due to changes...
[nodemon] starting `node index.js`
Server running on port 3000
💭 体验 :开发神器!不用每次改代码都手动重启了。类似的工具还有
pm2(生产环境用)。
🔍 查看全局安装路径
bash
npm root -g # 查看全局包安装路径
npm list -g # 查看已安装的全局包
五、知识总结
🧠 模块化思维导图
Node.js 模块化
│
├── 模块化概念
│ ├── 每个文件是一个独立模块
│ ├── 独立作用域,变量不污染
│ ├── 按需加载,代码复用
│ └── 使用特定标准导出/导入
│
├── 两种模块化标准
│ ├── CommonJS(Node.js 默认)
│ │ ├── 导出:module.exports = {}
│ │ └── 导入:require('模块名')
│ │
│ └── ECMAScript(ES6)
│ ├── 默认导出:export default {}
│ ├── 默认导入:import xxx from '...'
│ ├── 命名导出:export const xxx
│ └── 命名导入:import { xxx } from '...'
│
├── 包的概念
│ ├── 项目包:写业务逻辑
│ ├── 软件包:封装工具方法
│ └── 必须有 package.json
│
└── npm 管理器
├── npm init -y 初始化
├── npm i 包名 安装本地包
├── npm i 包名 -g 安装全局包
├── npm i 安装所有依赖
└── nodemon 自动重启工具
📋 两种模块化标准速查
| 操作 | CommonJS | ES Module |
|---|---|---|
| 导出单个 | module.exports = { a, b } |
export default { a, b } |
| 导出多个 | module.exports = { a, b } |
export const a = ... |
| 导入默认 | const obj = require('...') |
import obj from '...' |
| 导入命名 | const { a } = require('...') |
import { a } from '...' |
| 启用方式 | Node.js 默认 | package.json 设置 "type": "module" |
六、常用命令速查
🖥️ Node.js 命令
| 命令 | 作用 |
|---|---|
node 文件名.js |
运行 JS 文件 |
nodemon 文件名.js |
自动重启运行 |
📦 npm 命令
| 命令 | 作用 |
|---|---|
npm init |
初始化项目(交互式) |
npm init -y |
初始化项目(使用默认值)⭐ |
npm i 包名 |
安装本地软件包 ⭐ |
npm i 包名 -g |
安装全局软件包 ⭐ |
npm i |
安装 package.json 中所有依赖 ⭐ |
npm uninstall 包名 |
卸载软件包 |
npm list |
查看已安装的包 |
npm list -g |
查看全局安装的包 |
npm root -g |
查看全局包安装路径 |
⚠️ 注意事项
| 注意点 | 说明 |
|---|---|
| 文件夹名 | 不要用中文或特殊符号 |
| node_modules | 不要提交到 Git |
| 自定义模块 | 引入时要加 ./ |
| ES Module | 需要设置 "type": "module" |
❓ 疑问与待深入
💭 还需要进一步学习的内容:
- package.json 更多字段 :
scripts、devDependencies、peerDependencies等 - npm 镜像源:如何切换到淘宝镜像加速下载?
- npx 命令:临时执行包命令
- 模块循环依赖:怎么处理 A 依赖 B,B 又依赖 A?
- ESM 和 CJS 混用:一个项目能同时用两种标准吗?
- npm 包发布:如何发布自己的包到 npm?
📝 学习心得
这节课学到了 Node.js 最核心的模块化机制:
- 模块化让代码更有组织,避免了全局污染
- 两种标准各有适用场景,CommonJS 适合 Node.js 后端,ES Module 适合前端工程化
- 包的概念让代码可以复用,npm 让分享和使用代码变得简单
- 本地包 vs 全局包的区别要搞清楚,工具类的一般装全局
最重要的收获是理解了模块化的设计思想------把复杂系统拆分成独立、可复用的模块。这在任何编程语言中都是通用的思想!