第1章 1.5 npm 与 package.json 全面解析

🎯 开场:npm 是什么?为什么你离不开它?

上一章我们学会了用 importexport 在文件之间传递数据,像搭积木一样把代码拼起来。

但问题来了——积木从哪来?

你自己写一个「发送网络请求」的功能试试?从头写?怕是要写到明年。有没有现成的轮子能直接用?

这就是 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

配图1 - 配图1

装完之后,你会看到文件夹里多了一个 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"
}

配图2 - 配图2

你可以编辑这些字段,改成你自己的项目信息。

概念 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.pngIMG_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 让你临时用工具,用完就走,不占地方

延伸学习资源:

  1. npm 官方文档 —— 最权威,有中文版
  2. 《Node.js 实战》—— 进阶好书,第 3 章专门讲 npm 生态
  3. lodash 文档 —— 超级实用的工具库,值得花 10 分钟浏览

互动钩子:

你在写项目时,遇到过「这个功能肯定有人写过了」的瞬间吗?后来你是自己写的还是找到轮子了?评论区聊聊,老粉优先回复!


下章剧透:

你现在能「用 npm 装轮子」,也能「用 import/export 组合代码」了。

但你有没有想过——这些文件到底存在哪?能不能用代码自己读取、创建、修改文件夹里的文件?

下一章,我们来解决这个问题。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。