第1章 1.5 npm 与 package.json 全面解析
🎯 开场:npm 是什么?为什么你离不开它?
上一章我们学会了用 import 和 export 在文件之间传递数据,像搭积木一样把代码拼起来。
但问题来了——积木从哪来?
你自己写一个「发送网络请求」的功能试试?从头写?怕是要写到明年。有没有现成的轮子能直接用?
这就是 npm 要解决的问题。
想象一下:你刚搬进一个新厨房,锅碗瓢盆都要自己买。但有一个超大的「厨具超市」,里面啥都有——电饭煲、蒸锅、空气炸锅。你只需要报名字,超市就给你送到家。
npm 就是 JavaScript 世界的这个超市。
你写 npm install lodash,3秒钟后一个「计算工具库」就出现在你项目里,随时能用。
本文学完,你能做到:
- 3 分钟内把别人的轮子装进自己项目
- 学会管理项目依赖,不把代码写成一锅粥
- 跑起来一个真实的小工具(比如批量重命名文件)
🧱 基础:npm 和 package.json 核心概念
概念 1:npm 是什么?
npm = Node Package Manager,翻译过来就是「Node.js 包管理器」。
类比一下:
- Python 程序员用 pip install xxx
- Java 程序员用 Maven 下载 jar 包
- Node.js 程序员用 npm install xxx
为什么要用? 不想重复造轮子。网络请求、日期处理、文件操作...都有现成的代码包,直接拿来用。
怎么用? 先确保你装好了 Node.js(打开终端输入 node -v,能看到版本号就说明装好了)。
# 搜索一个包
npm search express
# 安装一个包(装到当前项目)
npm install express
# 安装并记录到 package.json
npm install express --save

装完之后,你会看到文件夹里多了一个 node_modules 文件夹,这里面就是别人写好的代码包。
概念 2:package.json 是什么?
package.json = 你的项目身份证 + 购物清单。
类比一下:
- 身份证:记录项目名、版本、作者
- 购物清单:记录你「需要哪些轮子」
为什么需要这个文件?
想象你搬家到新电脑,只需要把 package.json 复制过去,运行 npm install,所有依赖就自动装好了。不用把 node_modules 发来发去(那个文件夹动不动几百MB)。
来看一个最简 package.json:
{
"name": "my-cooking-app",
"version": "1.0.0",
"description": "小明的厨房小工具",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"lodash": "^4.17.21"
}
}
逐行解释:
name:项目名字(不能用中文和空格)version:版本号(格式是 主版本.次版本.修订号)main:入口文件,别人引用你这个包时,默认找这个文件scripts:快捷命令,把长命令起个短名字dependencies:生产环境依赖,项目上线后还需要用的包
概念 3:dependencies vs devDependencies
这两个经常让新手懵。
dependencies(生产依赖):项目跑起来必须有的包
- 比如你做个网站,用户访问时需要用到的功能
devDependencies(开发依赖):写代码时才用,上线后就没用了
- 比如测试工具、代码打包工具、ESLint 检查工具
类比一下:
- dependencies = 炒菜需要的食材(没食材做不了饭)
- devDependencies = 厨房电器(装修时才用,住进去就不需要了)
# 安装生产依赖(默认)
npm install express
# 安装开发依赖
npm install --save-dev jest
安装后 package.json 会变成:
{
"dependencies": {
"express": "^4.18.2"
},
"devDependencies": {
"jest": "^29.0.0"
}
}
概念 4:npm init 创建一个新项目
为什么要用? 手动创建 package.json 容易出错,npm init 自动生成标准格式。
怎么用?
# 在空文件夹里运行
npm init
# 快速创建(一路回车跳过配置)
npm init -y
运行后你会得到一个默认的 package.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"
}

你可以编辑这些字段,改成你自己的项目信息。
概念 5:npm scripts 是什么?
scripts 是给你自己用的快捷命令。
类比:你经常要洗碗,每次都喊「打开水龙头→挤洗洁精→搓碗→冲水→关水龙头」,太麻烦。不如给它起个名字叫「洗碗」,以后喊一声就行。
{
"scripts": {
"start": "node index.js",
"dev": "node --watch index.js",
"build": "node build.js",
"test": "jest"
}
}
然后终端里直接运行:
npm run start
# 或者简写(start/stop/test 可以省掉 run)
npm start
概念 6:npx 是什么?
npx = 临时用一下这个工具,用完就走,不占地方。
类比:你家不常来客人,没必要为每个客人买专用拖鞋。用完就扔,下次需要再借。
# 临时用 cowsay(打印 ASCII 牛说话)
npx cowsay "你好啊"
# 临时用 create-react-app 创建一个 React 项目
npx create-react-app my-app
不用全局安装,不用污染你的项目,用完就消失。
🔥 实战:3 个递进小项目
项目 1:5 分钟,用 npm 装一个工具来处理字符串(5 分钟)
场景:你需要把「hello world」变成「Hello World」。
不用 npm 的话:自己写函数处理首字母大写。
用 npm 的话:装个现成的工具,一行搞定。
# 创建项目
mkdir string-tool
cd string-tool
npm init -y
# 安装 lodash(超级实用的工具库)
npm install lodash
创建 index.js:
// 引入 lodash
const _ = require('lodash');
// 要处理的字符串
const rawText = "hello world from npm";
// _.startCase = 把每个单词首字母变大写
const result = _.startCase(rawText);
console.log("原文:", rawText);
console.log("处理后:", result);
运行:
node index.js
预期输出:
原文: hello world from npm
处理后: Hello World From Npm
解释:一行 require('lodash') 引入了 lodash 库,然后用 _.startCase() 直接做字符串转换。不用自己写循环,不用查 ASCII 码。
项目 2:15 分钟,用 npm 读取 CSV 文件并统计数据
场景:小明是班里的学习委员,有一份 scores.csv 文件记录了每个同学的分数:
name,chinese,math,english
小明,85,92,88
小红,90,78,95
小刚,76,85,90
需求:算出每个同学的平均分,并找出全班最高平均分。
第一步:装工具
mkdir class-scores
cd class-scores
npm init -y
npm install csv-parser
第二步:创建 scores.csv 文件
name,chinese,math,english
小明,85,92,88
小红,90,78,95
小刚,76,85,90
第三步:创建 analyze.js
const fs = require('fs');
const csv = require('csv-parser');
// 存储学生成绩
const students = [];
// 读取 CSV 文件
fs.createReadStream('scores.csv')
.pipe(csv())
.on('data', (row) => {
// 把每行数据转成数字
const student = {
name: row.name,
chinese: parseInt(row.chinese),
math: parseInt(row.math),
english: parseInt(row.english)
};
students.push(student);
})
.on('end', () => {
console.log("=== 全班成绩统计 ===\n");
// 计算每个同学的平均分
let topStudent = null;
let topAverage = 0;
students.forEach(student => {
const average = (student.chinese + student.math + student.english) / 3;
console.log(`${student.name}: 平均分 ${average.toFixed(1)} 分`);
if (average > topAverage) {
topAverage = average;
topStudent = student;
}
});
console.log(`\n🏆 全班最高平均分:${topStudent.name},${topAverage.toFixed(1)} 分`);
});
运行:
node analyze.js
预期输出:
=== 全班成绩统计 ===
小明: 平均分 88.3 分
小红: 平均分 87.7 分
小刚: 平均分 83.7 分
🏆 全班最高平均分:小明,88.3 分
解释:用 fs.createReadStream 读取文件,通过 csv-parser 解析成对象数组,最后遍历计算平均分。
项目 3:15 分钟,组合做个「文件夹批量重命名工具」
场景:你有一堆截图文件,名字乱七八糟,比如:
- 微信截图_20240101_120301.png
- Screenshot_2024-01-01_12-03-01.png
你想统一改成 IMG_001.png、IMG_002.png 这种格式。
第一步:创建项目
mkdir batch-rename
cd batch-rename
npm init -y
npm install fs-extra
第二步:创建测试文件夹和文件
mkdir test-folder
touch "test-folder/微信截图_20240101_120301.png"
touch "test-folder/Screenshot_2024-01-01_12-03-01.png"
touch "test-folder/photo_abc123.jpg"
第三步:创建 rename.js
const fs = require('fs-extra');
const path = require('path');
// 配置区
const sourceFolder = './test-folder';
const prefix = 'IMG_'; // 新文件名前缀
const startNum = 1; // 起始编号
async function batchRename() {
// 读取文件夹里所有文件
const files = await fs.readdir(sourceFolder);
console.log(`找到 ${files.length} 个文件,开始重命名...\n`);
let counter = startNum;
for (const file of files) {
// 获取文件扩展名
const ext = path.extname(file);
// 跳过非图片文件(简单判断)
if (!['.png', '.jpg', '.jpeg'].includes(ext.toLowerCase())) {
continue;
}
// 拼接新名字
const newName = `${prefix}${String(counter).padStart(3, '0')}${ext}`;
const oldPath = path.join(sourceFolder, file);
const newPath = path.join(sourceFolder, newName);
// 执行重命名
await fs.rename(oldPath, newPath);
console.log(`✅ ${file} → ${newName}`);
counter++;
}
console.log(`\n完成!共重命名 ${counter - startNum} 个文件`);
}
batchRename().catch(err => {
console.error("出错了:", err);
});
运行:
node rename.js
预期输出:
找到 3 个文件,开始重命名...
✅ 微信截图_20240101_120301.png → IMG_001.png
✅ Screenshot_2024-01-01_12-03-01.png → IMG_002.png
✅ photo_abc123.jpg → IMG_003.jpg
完成!共重命名 3 个文件
解释:用 fs-extra 读取文件夹和重命名文件,配合 path 处理路径和扩展名。核心逻辑就是一个 for...of 循环遍历文件,逐个改名。
💪 进阶:常见坑 + 调试技巧
坑 1:安装了包但 require 报错
❌ 错误:
const axios = require('axios'); // Error: Cannot find module 'axios'
原因:忘了运行 npm install
✅ 正确:
npm install axios
node index.js
坑 2:node_modules 传给了别人
❌ 错误:把整个 node_modules 文件夹上传到 Git,导致仓库几百MB。
✅ 正确:在项目根目录创建 .gitignore 文件:
node_modules/
解释:别人拿到你的代码,只需要 npm install,依赖自动重新下载。
坑 3:dependencies 和 devDependencies 混用
❌ 错误:把测试工具放进 dependencies
{
"dependencies": {
"jest": "^29.0.0" // ❌ 这是开发工具,不该放这
}
}
✅ 正确:
npm install --save-dev jest
{
"devDependencies": {
"jest": "^29.0.0" // ✅ 放这里
}
}
坑 4:npm install 卡住不动
原因:网络问题或者 npm 源太慢
✅ 解决:切换国内镜像源
npm config set registry https://registry.npmmirror.com
npm install
坑 5:package.json 里的版本号写错
❌ 错误:"lodash": "4.17.21"(固定版本)
✅ 正确:"lodash": "^4.17.21"
版本号前缀含义:
- ^4.17.21:接受 4.x.x 的所有更新(小更新)
- ~4.17.21:只接受 4.17.x 的更新(补丁更新)
- 4.17.21:固定这个版本(不推荐)
调试技巧:console.log 最实用
const data = { name: "小明", age: 18 };
// 打印整个对象
console.log("当前数据:", data);
// 打印带标签的多个值
console.log("名字:", data.name, "年龄:", data.age);
// 打印格式化字符串
console.log(`用户信息:${data.name},${data.age}岁`);
进阶调试:用 console.table 把数组对象变成表格:
const users = [
{ name: "小明", score: 85 },
{ name: "小红", score: 92 }
];
console.table(users);
✏️ 练习题
练习 1(2 分钟):换个工具试试
- 输入:把项目 1 里的
_.startCase换成_.toUpperCase - 预期输出:原文全部大写
- 提示:查看 lodash 文档,或者直接猜
_.toLowerCase是什么效果
练习 2(2 分钟):加个判断
- 输入:在项目 1 末尾加一行判断,如果字符串长度超过 10 个字,输出「字符串太长了」
- 预期输出:输入 "hello world" 时输出「字符串太长了」
- 提示:
_.size()可以计算字符串长度
练习 3(5 分钟):处理一份新数据
- 输入:新建一个
students.csv,内容是:
name,height,weight
小明,175,65
小红,160,50
小刚,180,75
- 预期输出:计算 BMI(体重/身高²)并输出最矮的同学的 BMI
- 提示:复用项目 2 的代码框架,改一下字段名和计算公式
练习 4(5 分钟):把项目 2 和项目 3 串起来
- 输入:用项目 2 读取的分数数据,根据平均分给文件命名:平均分 90+ 命名为
A_001.txt,80+ 命名为B_001.txt,其他命名为C_001.txt - 预期输出:3 个文件,分别叫 A_001.txt、B_001.txt、C_001.txt
- 提示:先排序,再循环命名
练习 5(3 分钟):读懂报错
- 输入:运行下面这段代码会报什么错?
const fs = require('fs');
const data = fs.readFileSync('not-exist.txt');
console.log(data);
- 预期输出:你应该能说出错误类型和原因
- 提示:文件不存在时,Node.js 会怎么反应?
作业:做一个「个人支出记录工具」
需求描述:做一个命令行工具,记录你每天花了多少钱,并能统计每周支出。
功能点:
1. 用 fs.appendFile 保存支出记录到 expenses.txt(格式:日期,分类,金额)
2. 用 fs.readFileSync 读取记录并解析
3. 统计并输出「本周总支出」和「最高支出分类」
加分项:
1. 支持按月份查询
2. 支持删除指定记录
验收标准:
- 能运行 node expense.js add 餐饮 50 添加一笔支出
- 能运行 node expense.js summary 输出本周统计
- 代码有注释,说明每一步在干嘛
提交方式:把代码贴在评论区,或者发 GitHub 链接
📚 总结 + 资源
本文学了 3 件事:
1. npm 是 JavaScript 世界的「超市」,用 npm install 装轮子
2. package.json 是项目身份证 + 购物清单,让项目可迁移
3. npx 让你临时用工具,用完就走,不占地方
延伸学习资源:
互动钩子:
你在写项目时,遇到过「这个功能肯定有人写过了」的瞬间吗?后来你是自己写的还是找到轮子了?评论区聊聊,老粉优先回复!
下章剧透:
你现在能「用 npm 装轮子」,也能「用 import/export 组合代码」了。
但你有没有想过——这些文件到底存在哪?能不能用代码自己读取、创建、修改文件夹里的文件?
下一章,我们来解决这个问题。

评论(0)