前端开发绕不开:NPM包管理器完全指南(2026实战版)
前端开发绕不开:NPM包管理器完全指南(2026实战版)
前端开发绕不开:NPM包管理器完全指南(2026实战版)
说实话,我刚开始写前端那会儿,对npm的态度就八个字:能用就行,别问原理。那时候觉得不就是npm install嘛,敲完回车等进度条跑完,完事儿。直到后来踩了无数个坑——项目在别人电脑上跑不起来、生产环境莫名其妙报错、node_modules文件夹膨胀到把硬盘撑爆——我才意识到,这玩意儿水深得很。今天就把我这些年攒的血泪经验掏出来,争取让你看完少熬几个通宵。
先唠唠为啥你得搞懂这玩意儿
我记得特别清楚,2019年那会儿接了个老项目,前任老哥早就离职了,留下一坨"遗产"。我clone下来,npm install,然后npm start,报错。改报错,再报错。折腾到凌晨三点,发现是某个依赖的版本和Node版本不兼容。那时候我就悟了:npm真不是npm install就完事儿的。
你现在去面试,要是面试官问你"dependencies和devDependencies啥区别",你答不上来,场面会很尴尬。再比如团队开发,你不提交lock文件,队友拉代码跑不起来,群里@你,你尴不尴尬?再比如生产环境部署,你把开发依赖也打包进去了,镜像体积多出来几百兆,运维老哥会不会想刀你?
搞明白npm,至少能让你少踩80%的坑。这不是夸张,是我用头发换来的经验。
NPM到底是个啥东西
别被Node Package Manager这个英文名唬住,翻译过来就是"Node包管理器"。这老家伙2009年就诞生了,比React、Vue这些框架资历深多了。它跟着Node.js一起安装,属于标配工具,你不用额外下载。
现在npm托管了超过200万个包,是全球最大的代码仓库。你可以把它理解成前端的"应用商店"——想要啥功能,上去搜,安装,直接用。想做个轮播图?有swiper。要发HTTP请求?axios等着你。甚至连生成假数据、压缩图片、检查代码规范,都有人帮你写好了。
但问题在于,应用商店里的App也有好坏之分,有的常年不更新,有的带着恶意代码,有的和你手机系统不兼容。npm包也一样,用之前得长点心。
这玩意儿核心能干哪些活
很多人以为npm就是用来安装第三方库的,格局小了。它其实是个全能管家:
第一,安装管理依赖。这是基本功,React、Vue、lodash这些全靠它。但你要知道,它不只是把代码下载下来,还要处理依赖的依赖、版本的匹配、平台的兼容性。
第二,版本控制。package.json里那些^2.1.0、~3.0.0不是乱写的,它们决定了你项目用的是哪个版本的包。搞错了,轻则功能异常,重则项目跑不起来。
第三,脚本自动化。npm scripts能让你把打包、测试、代码检查这些操作串起来,一键执行。前端工程化很大程度上就是靠它撑起来的。
第四,发布分享。你自己写了个好用的工具,可以通过npm发布到仓库,让全世界用。很多开源大佬就是这么起步的。
第五,安全审计。npm audit能帮你检查依赖有没有已知漏洞,这在企业级项目里特别重要。
package.json这文件你得往死里研究
这文件是项目的身份证,没它npm都不知道咋干活。我第一次看到它的时候,觉得就是个配置列表,后来才发现里面的门道深得很。
基础结构长这样
{
"name": "my-awesome-project",
"version": "1.0.0",
"description": "一个牛逼的项目",
"main": "index.js",
"scripts": {
"start": "node index.js",
"build": "webpack --mode production",
"test": "jest"
},
"dependencies": {
"express": "^4.18.0",
"lodash": "~4.17.21"
},
"devDependencies": {
"webpack": "^5.0.0",
"jest": "^29.0.0"
},
"engines": {
"node": ">=14.0.0",
"npm": ">=6.0.0"
}
}
dependencies和devDependencies别搞混了
这俩的区别很多人说不清楚,其实很简单:
- dependencies:生产环境需要的。比如你用React做项目,用户浏览器里得跑React代码,这就是生产依赖。
- devDependencies:只有开发时需要。比如webpack、babel、测试框架,这些东西最后不会打包到给用户的产品里。
分清楚能省不少空间。你想啊,要是把jest、eslint这些全打包进生产环境,用户得多下载几十兆无用代码,加载速度慢了,体验差了,你老板得找你谈话。
# 安装生产依赖(默认)
npm install express
# 安装开发依赖
npm install webpack --save-dev
# 或者简写
npm install webpack -D
version字段的语义化版本规则得门儿清
版本号格式是主版本.次版本.修订版,比如2.1.3:
- 主版本(Major):破坏性更新,可能不兼容旧代码。比如React 17升到18,有些API就变了。
- 次版本(Minor):新增功能,但向下兼容。比如加了新特性,你原来的代码还能跑。
- 修订版(Patch):修bug,不增加新功能。
那^和~是啥意思?这是很多人栽跟头的地方:
^2.1.0:允许更新到2.x.x的最新版,但不包括3.0.0。也就是说,可以自动升级到2.9.9,但不会升到3.0.0。~2.1.0:只允许更新到2.1.x的最新版,也就是最多到2.1.9,不会到2.2.0。
{
"dependencies": {
"lodash": "^4.17.21", // 可以自动更新到4.24.0,但不会到5.0.0
"axios": "~1.2.0" // 可以自动更新到1.2.9,但不会到1.3.0
}
}
scripts区域能写啥,看完你会回来感谢我
scripts是npm最被低估的功能之一。你可以在这里定义各种命令,然后用npm run xxx执行。
{
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js",
"build": "webpack --mode production",
"build:dev": "webpack --mode development",
"test": "jest",
"test:watch": "jest --watch",
"lint": "eslint src/",
"lint:fix": "eslint src/ --fix",
"clean": "rm -rf dist node_modules",
"reinstall": "npm run clean && npm install",
"precommit": "npm run lint && npm run test"
}
}
看到那个precommit了吗?这是Git hooks的玩法,提交代码前自动跑lint和测试,保证代码质量。还有reinstall,一键清理重装,专治各种疑难杂症。
别手贱乱改,改错了解释成本很高
我见过有人直接把package.json里的版本号手动改成latest,觉得这样能永远用最新版。结果呢?某天React升了大版本,项目直接崩了,全组人陪他加班回滚。还有删掉main字段的,导致别人require他的包时找不到入口文件。
改之前先想想,这个字段是干嘛的?删了会有什么后果?不确定就查文档,别凭感觉。
常用命令我给你们捋一捋
npm init 初始化项目
新建项目第一步永远是这个。它会问你一堆问题:项目叫啥、版本号多少、作者是谁。如果你嫌烦,加-y直接跳过,用默认值生成。
# 交互式初始化,一步一步填信息
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"
}
npm install 安装包
这是最常用的命令,但细节很多。
# 安装指定包,并添加到dependencies
npm install express
# 安装指定版本
npm install express@4.17.1
# 安装开发依赖
npm install webpack --save-dev
# 全局安装(慎用!后面会讲为啥)
npm install -g @vue/cli
# 根据package.json安装所有依赖
npm install
# 或者简写
npm i
安装时npm会做这几件事:
- 检查package.json,看看要装哪些包
- 解析版本范围,确定具体装哪个版本
- 下载包文件,放到node_modules
- 更新package.json和package-lock.json
npm install -g 全局安装,慎用别啥都全局
全局安装的包,可以在命令行任何位置使用。比如@vue/cli全局安装后,你可以在任何目录运行vue create my-project。
但别啥都全局装。第一,全局包不会出现在项目的package.json里,队友不知道你用了啥。第二,不同项目可能需要不同版本的工具,全局只能装一个版本,容易冲突。第三,全局包的权限问题经常导致安装失败。
建议:只在确实需要全局使用的工具上用-g,比如create-react-app、@angular/cli这种脚手架。项目特有的依赖(比如webpack配置)一律本地安装。
npm uninstall 卸载依赖
# 卸载并更新package.json
npm uninstall lodash
# 卸载开发依赖
npm uninstall jest --save-dev
记得清理package.json,有时候你以为卸载了,其实配置里还残留着,导致别人装了一堆没用的包。
npm update 更新包
# 更新所有包到允许的最新版本(根据^和~规则)
npm update
# 更新指定包
npm update lodash
警告:更新前先看changelog,特别是主版本号变化的时候。我见过有人直接npm update,结果React 17升到18,Hooks的API变了,全项目报错。
npm run 运行脚本
# 运行scripts里定义的命令
npm run build
npm run test
# start和test可以省略run
npm start # 等同于 npm run start
npm test # 等同于 npm run test
npm audit 安全审计,这功能很多人不知道
# 检查依赖漏洞
npm audit
# 自动修复能修的漏洞(主要是升级版本)
npm audit fix
# 强制修复,可能包含破坏性更新
npm audit fix --force
这在企业项目里特别重要。2021年那个著名的colors包被植入恶意代码事件,就是因为作者对npm不满,故意搞破坏。如果你的项目依赖了那个版本,npm audit能帮你揪出来。
依赖版本那些破事儿
^和~符号啥意思,多少人在这栽过跟头
前面简单提过,这里再详细说说。假设有个包当前版本是2.1.3:
^2.1.3:兼容2.x.x,也就是>=2.1.3且<3.0.0~2.1.3:兼容2.1.x,也就是>=2.1.3且<2.2.02.1.3:锁定2.1.3,一点都不能变
实际案例:
{
"dependencies": {
"lodash": "^4.17.21"
}
}
今天安装,可能装的是4.17.21。一个月后同事安装,可能装的是4.24.0(如果lodash更新了)。这时候你们用的版本不一样,行为可能有细微差别,debug时能把你逼疯。
锁定版本用package-lock.json,别删别删别删
这就是lock文件存在的意义。它记录了实际安装的精确版本号,以及所有依赖的确切解析路径。
当你第一次npm install时,npm会:
- 根据package.json里的版本范围,确定具体版本
- 把这个具体版本写进package-lock.json
- 以后所有人安装,都按lock文件里的版本来,保证一致
千万别删这个文件! 我见过有人觉得它"自动生成的不重要",直接gitignore掉了。结果呢?测试环境跑得好好的,生产环境部署时装了新版本的某个包,API变了,线上直接报错。
语义化版本规则,主版本.次版本.修订版
版本号格式MAJOR.MINOR.PATCH:
- MAJOR:不兼容的API修改,比如React 17→18
- MINOR:向下兼容的功能新增,比如加了新组件
- PATCH:向下兼容的问题修复,比如修了个bug
还有预发布版本:
2.0.0-alpha.1:内测版2.0.0-beta.2:公测版2.0.0-rc.1:候选发布版
安装时可以指定:
npm install react@next # 安装最新预发布版
npm install react@18.0.0-beta.1 # 安装指定预发布版
为啥别人拉代码跑不起来,大概率是版本问题
经典场景:
- 你项目用了Node 16,同事电脑是Node 12
- 某个包在Node 12上不兼容
- 同事
npm install报错,群里@你
解决方案是在package.json里指定engines:
{
"engines": {
"node": ">=14.0.0",
"npm": ">=6.0.0"
}
}
还可以加个强制检查:
{
"engineStrict": true
}
这样版本不对时,npm会直接报错,而不是装完才发现跑不起来。
团队开发必须提交lock文件,这是底线
这是团队协作的铁律:
- 必须提交:package-lock.json(npm)、yarn.lock(yarn)、pnpm-lock.yaml(pnpm)
- 必须忽略:node_modules
为什么?因为lock文件保证了"我电脑上能跑,你电脑上也能跑"。node_modules则不提交,因为体积太大,而且不同操作系统、不同架构的包二进制文件不一样,提交上去也没用。
.gitignore应该长这样:
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.DS_Store
.env
但不要把lock文件加进去!
实际项目里咋用才顺手
新建项目第一步永远是npm init
别偷懒,哪怕是小demo也初始化一下。这样你才能用npm管理依赖,而不是手动下载js文件放到html里。
完整流程:
# 1. 创建目录
mkdir my-project && cd my-project
# 2. 初始化
npm init -y
# 3. 安装核心依赖
npm install react react-dom
# 4. 安装开发依赖
npm install webpack webpack-cli babel-loader @babel/core @babel/preset-react --save-dev
# 5. 创建基础文件
touch index.js index.html webpack.config.js
# 6. 配置scripts
# 在package.json里添加:
# "scripts": {
# "build": "webpack --mode production",
# "dev": "webpack serve --mode development"
# }
安装框架依赖记得区分生产和开发环境
React项目典型依赖结构:
{
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.8.0",
"axios": "^1.3.0"
},
"devDependencies": {
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@vitejs/plugin-react": "^3.1.0",
"typescript": "^4.9.0",
"vite": "^4.1.0",
"eslint": "^8.34.0",
"prettier": "^2.8.0"
}
}
看到没?vite、eslint、prettier这些只在开发时用,用户不需要。axios、react-router这些用户浏览器里要跑的,放dependencies。
配置自定义脚本让打包测试自动化
一个成熟的前端项目,scripts应该覆盖完整的工作流:
{
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"test": "vitest",
"test:ui": "vitest --ui",
"coverage": "vitest run --coverage",
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"lint:fix": "eslint src --ext ts,tsx --fix",
"format": "prettier --write \"src/**/*.{ts,tsx,css,json}\"",
"format:check": "prettier --check \"src/**/*.{ts,tsx,css,json}\"",
"type-check": "tsc --noEmit",
"prepare": "husky install",
"precommit": "lint-staged",
"clean": "rm -rf dist node_modules/.vite",
"reinstall": "rm -rf node_modules package-lock.json && npm install"
}
}
解释一下:
prepare:安装依赖后自动执行,这里用来初始化husky(Git hooks工具)precommit:提交前自动跑lint和格式化type-check:只检查类型,不生成文件,速度快clean:清理缓存,有时候vite的缓存会导致奇怪的问题
多项目用npm workspaces管理,少装好多遍
如果你有个大项目,里面包含多个子项目(比如一个UI库+一个文档站+一个示例项目),每个子项目都有自己的node_modules,那磁盘空间爆炸,依赖版本还可能不一致。
npm workspaces能解决这个问题。在项目根目录的package.json里配置:
{
"name": "my-monorepo",
"private": true,
"workspaces": [
"packages/*",
"apps/*"
],
"scripts": {
"build": "npm run build --workspaces",
"test": "npm run test --workspaces",
"clean": "rm -rf node_modules **/node_modules"
}
}
目录结构:
my-monorepo/
├── package.json # 根配置
├── node_modules/ # 共享的依赖放这里
├── packages/
│ ├── ui-components/ # UI组件库
│ │ ├── package.json
│ │ └── src/
│ └── utils/ # 工具函数库
│ ├── package.json
│ └── src/
└── apps/
├── docs/ # 文档站点
│ ├── package.json
│ └── src/
└── admin/ # 后台管理系统
├── package.json
└── src/
在根目录运行npm install,所有子项目的依赖都会装到根node_modules里(如果版本兼容),子项目之间也可以互相引用。
私有包怎么发布,公司内部复用代码神器
有时候你写了个工具,不想开源,但多个项目要用。可以发布到私有npm仓库。
方案一:npm私有包(付费)
npm官网支持私有包,$7/月/用户,适合小团队。
# 登录npm账号
npm login
# 发布私有包
npm publish --access restricted
方案二:自建npm仓库(免费)
用Verdaccio,轻量级,配置简单:
# 全局安装Verdaccio
npm install -g verdaccio
# 启动服务
verdaccio
# 配置npm使用本地仓库
npm set registry http://localhost:4873
# 发布包
npm publish
方案三:Git仓库直接安装
最简单,不用搭建仓库:
{
"dependencies": {
"my-utils": "git+https://github.com/mycompany/my-utils.git#v1.2.0",
"my-utils-ssh": "git+ssh://git@github.com:mycompany/my-utils.git#main"
}
}
甚至可以装本地路径:
{
"dependencies": {
"my-local-pkg": "file:../my-local-pkg"
}
}
这在开发调试时特别有用。
这玩意儿有啥毛病你得知道
安装速度慢,尤其在国内网络环境下
npm默认仓库在国外,不翻墙的话,安装大项目能急死人。200多个依赖,每个都要握手、下载、解压,网速慢的时候半小时起步。
node_modules体积爆炸,动不动就几百兆
这是个经典梗了。node_modules的体积能大到什么程度?有人做过统计,一个普通的React项目,node_modules轻松上500MB,而项目源码可能只有几MB。
为啥这么大?因为依赖嵌套。A依赖B,B依赖C,C又依赖D,每个都要装一遍。而且很多包带了测试文件、文档、TypeScript定义,这些生产环境都不需要,但npm默认全给你装上。
依赖嵌套深,排查问题能把你绕晕
运行npm ls,输出可能是这样的:
my-project@1.0.0 /Users/me/my-project
├─┬ react@18.2.0
│ ├─┬ loose-envify@1.4.0
│ │ └── js-tokens@4.0.0
│ └── scheduler@0.23.0
├─┬ webpack@5.75.0
│ ├─┬ @types/eslint-scope@3.7.4
│ │ ├─┬ @types/eslint@8.21.0
│ │ │ └── @types/estree@0.0.51
│ │ └── @types/estree@0.0.51 deduped
│ ├─┬ @types/estree@0.0.51
... 还有几百行
排查问题时,你得一层层找,这个版本对不对?有没有重复安装?头都大了。
恶意包风险,15万多个坏包在仓库里潜伏
npm是开放的,任何人都能发布包。这就给了坏人可乘之机。有故意植入恶意代码的,有 typo-squatting(名字拼写像热门包,比如lodash vs lodahs)的,有废弃包被劫持的。
2022年有个案例,一个叫node-ipc的包被作者植入恶意代码,检测到用户IP在俄罗斯或白俄罗斯时,会删除用户文件。很多项目无辜中招。
版本冲突地狱,peerDependencies能让人崩溃
peerDependencies是声明"我需要宿主项目提供这个依赖"。比如React插件会声明:
{
"peerDependencies": {
"react": ">=16.8.0"
}
}
意思是"我的宿主项目必须装了React,且版本不低于16.8.0"。
但如果你的项目装了React 17,某个插件要React 16,另一个插件要React 18,这就冲突了。npm会警告,但不一定能解决,你得手动协调版本,有时候根本协调不了。
跟yarn和pnpm比咋样
yarn是Facebook出的,当年就是为了解决npm慢
2016年yarn发布时,npm确实慢且不稳定。yarn带来了:
- 并行下载(速度快几倍)
- lock文件(yarn.lock,比npm早期的shrinkwrap好用)
- 离线模式(装过的包缓存起来,下次离线也能装)
但现在npm也追上来了,npm 5+有了package-lock.json,速度也优化了不少。
pnpm最省空间,硬链接机制真的香
pnpm是后来者,但解决了一个核心问题:node_modules体积。
它用硬链接(hard link)机制,所有项目的依赖都存到全局仓库,node_modules里只放链接。同样200MB的依赖,装10个项目,npm要占2GB,pnpm只占200MB+一点链接的空间。
而且pnpm的node_modules结构是平的,不会无限嵌套,排查问题清爽多了。
# 安装pnpm
npm install -g pnpm
# 用法和npm几乎一样
pnpm install
pnpm add react
pnpm run build
npm胜在生态最成熟,文档和社区资源最多
虽然yarn和pnpm各有优势,但npm依然是生态最完善的。遇到问题时,搜"npm xxx"能找到更多答案。很多CI/CD工具、Docker镜像,默认都是npm。
现在npm速度也上来了,差距没那么大
npm 7+版本引入了很多优化,比如:
- 并行下载
- 更好的缓存机制
- workspaces支持
现在小项目用npm和yarn,速度差别不大。大项目可能yarn/pnpm快一点,但也没到必须换的程度。
选哪个看团队习惯,别为了换而换
我的建议:
- 新项目:可以试试pnpm,省空间是真的爽
- 老项目:别折腾,继续用npm或yarn,迁移成本可能很高
- 团队协作:统一工具,别有人用npm有人用yarn,lock文件会冲突
翻车了咋排查我给你支几招
删掉node_modules和lock文件重装,万能第一招
90%的奇怪问题都能用这招解决:
# 一键清理重装
rm -rf node_modules package-lock.json
npm install
原理是清除所有缓存和已安装包,重新解析依赖树。有时候node_modules里的文件损坏了,或者lock文件记录了错误状态,重装就正常了。
检查网络,换淘宝镜像源能解决一半问题
如果安装特别慢,或者总是超时,换国内镜像:
# 临时使用淘宝镜像
npm install --registry=https://registry.npmmirror.com
# 永久设置
npm config set registry https://registry.npmmirror.com
# 检查当前镜像
npm config get registry
权限问题用sudo,但别养成习惯
有时候全局安装会报权限错误,有人直接用sudo npm install -g,这能解决问题,但不推荐。因为用sudo装的包,以后普通用户可能没法更新或删除。
正确做法是改npm默认目录:
# 创建全局安装目录
mkdir ~/.npm-global
# 配置npm使用新目录
npm config set prefix '~/.npm-global'
# 添加到PATH
echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
版本冲突看npm ls,依赖树一目了然
# 查看完整依赖树
npm ls
# 只看顶层依赖
npm ls --depth=0
# 查看特定包的版本
npm ls react
# 查看为什么装了这个包(谁依赖的)
npm ls react --why
清缓存npm cache clean,有时候真管用
# 清除缓存
npm cache clean --force
# 验证缓存完整性
npm cache verify
缓存损坏会导致安装失败或装错版本,清理一下往往有奇效。
看错误日志别跳过,答案通常就在里面
npm报错时,别只看最后一行错误摘要,往上翻,通常会有具体的错误原因。比如:
EACCES:权限问题ECONNREFUSED:网络问题ENOENT:文件找不到,可能是路径太长(Windows经典问题)EBADPLATFORM:平台不兼容,比如mac-only的包在Windows上装
国内网络慢到怀疑人生咋整
配置淘宝镜像源,npm config set registry
前面提过,这里给个完整的配置:
# 设置淘宝镜像
npm config set registry https://registry.npmmirror.com
# 设置其他常用镜像(electron、node-sass等)
npm config set electron_mirror https://npmmirror.com/mirrors/electron/
npm config set sass_binary_site https://npmmirror.com/mirrors/node-sass/
npm config set phantomjs_cdnurl https://npmmirror.com/mirrors/phantomjs/
npm config set chromedriver_cdnurl https://npmmirror.com/mirrors/chromedriver/
用nrm工具快速切换源,比手动改方便
nrm是npm registry manager,可以快速切换镜像源:
# 安装nrm
npm install -g nrm
# 列出可用源
nrm ls
# 切换源
nrm use taobao
# 测试速度
nrm test
公司内网搭私有仓库,一劳永逸
如果公司有防火墙,外网访问受限,可以搭个私有仓库(前面提到的Verdaccio),把常用的包缓存到内网。
离线安装提前下载好,没网也能干活
有时候要去客户现场部署,没外网,可以提前把依赖打包:
# 在有网环境打包依赖
npm pack
# 或者直接用npm-bundle
npm install -g npm-bundle
npm-bundle
# 到离线环境解压安装
tar -xzf my-project-deps.tgz
npm install --cache ./npm-cache --optional --cache-min 99999999999 --shrinkwrap false my-project-deps.tgz
代理配置搞对,企业网络必备技能
公司网络有代理的话,要配置npm:
# 设置代理
npm config set proxy http://proxy.company.com:8080
npm config set https-proxy http://proxy.company.com:8080
# 如果代理需要认证
npm config set proxy http://username:password@proxy.company.com:8080
# 取消代理
npm config delete proxy
npm config delete https-proxy
安全这事儿别不当回事
npm audit定期跑,有漏洞赶紧修
# 检查漏洞
npm audit
# 自动修复
npm audit fix
# 查看详细报告
npm audit --json
建议加到CI流程里,每次提交自动检查。
别随便install来路不明的包
安装前检查:
- 包名对不对?有没有 typo?
- 下载量多少?周下载量几百的要小心
- 最后更新时间?两年没更新的可能已废弃
- GitHub仓库有没有?代码质量如何?
# 查看包信息
npm view lodash
# 查看包详情(包括依赖、版本历史)
npm info react
检查包维护者是否活跃,僵尸包风险高
去GitHub看:
- 最近有没有commit?
- issue有没有人回复?
- PR有没有人合并?
如果仓库已经荒废,考虑找替代品。
敏感信息别写进package.json
// 错误示范!
{
"scripts": {
"deploy": "scp -r dist/ user:password@server:/var/www/"
}
}
密码直接写脚本里,提交到git,全公司都能看到。应该用环境变量:
{
"scripts": {
"deploy": "scp -r dist/ $DEPLOY_USER@$DEPLOY_HOST:/var/www/"
}
}
依赖太多也是风险,能不用就不用
每个依赖都是潜在的攻击面。lodash一个工具函数库,被人找出漏洞的次数不少。如果只需要一个深拷贝功能,自己写几行代码比引入整个lodash更安全。
老手都在用的高阶玩法
npx临时执行包,不用安装就能用
# 不用全局安装create-react-app,直接运行
npx create-react-app my-app
# 运行特定版本的包
npx react@17.0.2 --version
# 执行本地node_modules里的命令(npm 5.2.0+自带)
npx eslint src/
这在想试用某个工具,又不想污染全局环境时特别好用。
npm link本地调试,开发自己的包必备
假设你在开发一个工具包my-utils,同时想在另一个项目my-app里测试:
# 在my-utils目录
cd ~/projects/my-utils
npm link # 创建全局链接
# 在my-app目录
cd ~/projects/my-app
npm link my-utils # 链接到本地包
# 现在my-app里用的my-utils就是本地最新代码
# 修改my-utils,my-app里实时生效
调试完记得取消链接:
cd ~/projects/my-app
npm unlink my-utils
npm install # 重新装回npm仓库的版本
workspaces管理多项目,monorepo方案
前面提过,这里给个更完整的例子。假设你在做一个UI组件库,包含:
packages/core:核心组件packages/icons:图标库apps/docs:文档站点apps/playground:测试 playground
根目录package.json:
{
"name": "my-ui-monorepo",
"private": true,
"workspaces": [
"packages/*",
"apps/*"
],
"scripts": {
"build": "npm run build --workspaces",
"build:core": "npm run build --workspace=packages/core",
"dev": "npm run dev --workspace=apps/docs",
"test": "npm run test --workspaces",
"lint": "eslint .",
"clean": "rm -rf node_modules **/node_modules **/dist"
},
"devDependencies": {
"eslint": "^8.0.0",
"prettier": "^2.0.0",
"typescript": "^4.9.0"
}
}
packages/core/package.json:
{
"name": "@myui/core",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"test": "jest"
},
"dependencies": {
"@myui/icons": "^1.0.0"
}
}
apps/docs/package.json:
{
"name": "@myui/docs",
"version": "1.0.0",
"scripts": {
"dev": "vitepress dev",
"build": "vitepress build"
},
"dependencies": {
"@myui/core": "^1.0.0",
"vitepress": "^1.0.0"
}
}
这样配置后:
- 在根目录
npm install,所有子项目的依赖都装好 @myui/core里可以直接import { Icon } from '@myui/icons',因为workspaces会自动链接本地包- 发布时,先发布
icons,再发布core,最后发布docs(如果只是站点就不用发布)
发布包到npm,步骤我给你们拆细了
第一步:准备
# 登录npm(没有账号先去npmjs.com注册)
npm login
# 检查登录状态
npm whoami
第二步:配置package.json
{
"name": "my-awesome-utils", // 必须是全局唯一的名字
"version": "1.0.0", // 每次发布要递增
"description": "一些好用的工具函数",
"main": "dist/index.js", // 入口文件
"types": "dist/index.d.ts", // TypeScript定义
"files": [ // 哪些文件会打包发布
"dist",
"README.md",
"LICENSE"
],
"scripts": {
"build": "tsc",
"prepublishOnly": "npm run build && npm test"
},
"keywords": ["utils", "helpers"],
"author": "Your Name <you@example.com>",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/you/my-awesome-utils.git"
},
"bugs": {
"url": "https://github.com/you/my-awesome-utils/issues"
}
}
第三步:构建和测试
# 确保能正常构建
npm run build
# 确保测试通过
npm test
# 本地预览会发布哪些文件
npm pack
# 这会生成一个tgz文件,解压看看里面是不是你想要的
第四步:发布
# 发布公开包(免费)
npm publish --access public
# 发布私有包(需要付费账号)
npm publish --access restricted
# 发布beta版
npm version prerelease --preid=beta # 版本变成1.0.1-beta.0
npm publish --tag beta
第五步:验证
# 在另一个目录测试安装
mkdir test-install && cd test-install
npm init -y
npm install my-awesome-utils
node -e "console.log(require('my-awesome-utils'))"
配置.npmrc个性化设置,提升效率
.npmrc文件可以配置npm的行为,项目根目录放一个叫.npmrc的文件:
# 使用淘宝镜像
registry=https://registry.npmmirror.com
# 保存精确版本,不要^和~
save-exact=true
# 安装时自动添加为开发依赖(默认是生产依赖)
save-dev=true
# 禁止生成package-lock.json(不推荐,但某些场景需要)
package-lock=false
# 设置脚本权限(允许运行postinstall脚本)
unsafe-perm=true
# 严格SSL检查(企业内网证书可能有问题时设为false)
strict-ssl=true
# 日志级别
loglevel=http
也可以放用户目录(~/.npmrc),对所有项目生效。
那些我踩过你们别踩的坑
别把node_modules提交到git,丢人就在这
这看起来是常识,但真有人干过。node_modules动辄几百兆,提交到git仓库,clone时要下半天,而且完全没必要。
.gitignore必须加:
node_modules/
如果已经提交了,怎么救?
# 1. 删除git缓存中的node_modules(但不删除本地文件)
git rm -r --cached node_modules
# 2. 提交删除
git commit -m "Remove node_modules from git"
# 3. 添加到.gitignore
echo "node_modules/" >> .gitignore
git add .gitignore
git commit -m "Add node_modules to gitignore"
lock文件一定要提交,不然队友会骂你
前面强调过,再强调一遍。package-lock.json、yarn.lock、pnpm-lock.yaml,这些必须提交。node_modules必须忽略。
全局安装包别太多,容易出冲突
全局包只能有一个版本,但不同项目可能需要不同版本。比如项目A需要Vue CLI 4,项目B需要Vue CLI 5,全局只能装一个,切换很麻烦。
建议:全局只装最基础的脚手架(如create-react-app、@angular/cli),项目特定的工具(如webpack、vite)都本地安装。
生产环境别用devDependencies里的东西
// 错误示范!
// 假设eslint是devDependencies
const eslint = require('eslint'); // 生产环境会报错,因为没装!
// 正确做法:只在开发时使用
// .eslintrc.js 或 scripts里用,不要业务代码里require
升级大版本前先读changelog,别头铁
React 17升18,Vue 2升3,Webpack 4升5,这些大版本升级都有破坏性变更。直接npm update可能会让项目崩掉。
正确姿势:
- 先读官方迁移指南(Migration Guide)
- 在分支上升级,测试通过再合并
- 使用工具检查兼容性,比如
react-codemod
效率提升小妙招随便拿
用nvm管理Node版本,多项目切换不头疼
不同项目可能需要不同Node版本,nvm(Node Version Manager)能帮你快速切换:
# 安装nvm(Mac/Linux)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
# 安装特定版本
nvm install 16
nvm install 18
# 切换版本
nvm use 16
nvm use 18
# 设置默认版本
nvm alias default 18
# 项目目录自动切换(在项目根目录创建.nvmrc文件)
echo "16.14.0" > .nvmrc
# 进入目录时运行
nvm use
Windows用户用nvm-windows。
配置npm脚本别名,少打好多字
在~/.bashrc或~/.zshrc里加:
# npm别名
alias ni='npm install'
alias nid='npm install --save-dev'
alias nig='npm install -g'
alias nr='npm run'
alias ns='npm start'
alias nb='npm run build'
alias nt='npm test'
alias nl='npm run lint'
alias nc='npm run clean'
alias nrm='rm -rf node_modules package-lock.json && npm install'
现在你可以:
ni # npm install
nid webpack # npm install webpack --save-dev
nr build # npm run build
nrm # 清理重装
用npm-check-updates检查可更新的包
# 安装
npm install -g npm-check-updates
# 检查更新
ncu
# 输出示例:
# @types/node ^16.0.0 → ^18.0.0
# eslint ^7.0.0 → ^8.0.0
# typescript ^4.0.0 → ^4.9.0
# 交互式选择更新
ncu -i
# 直接更新package.json
ncu -u
.npmrc配置文件放家目录,全局生效
前面提过项目级的.npmrc,你也可以在用户目录(~/.npmrc)放全局配置,对所有项目生效:
# 查看当前配置
npm config list
# 查看全局配置文件位置
npm config get userconfig
# 编辑全局配置
npm config edit
学会看package.json里的bin字段,很多工具藏在这
很多npm包提供命令行工具,比如webpack、eslint、tsc。它们是怎么注册为命令的?靠package.json里的bin字段:
{
"name": "my-cli-tool",
"bin": {
"my-tool": "./bin/cli.js"
}
}
安装后,npm会在node_modules/.bin/目录下创建链接,你就可以运行my-tool了。本地安装的包,用npx运行,其实就是找这个目录。
# 查看项目里有哪些可用命令
ls node_modules/.bin/
# 或者直接运行
npx my-tool
最后唠点实在的
写到这儿,基本上把我这些年关于npm的经验都倒出来了。说实话,这工具真没啥神秘的,就是个包管理器,用多了自然就会。但里面的细节确实多,版本管理、依赖解析、脚本配置、安全审计,每个点都能展开讲半天。
别光收藏不实践,装一遍比看十遍管用。建议你找个小项目,从npm init开始,一步步装依赖、配脚本、调版本,踩几个坑就全明白了。
遇到问题别慌,99%的情况别人都遇到过,Google一下,Stack Overflow上基本都有答案。要是搜不到,去GitHub提issue,开源社区的人通常很乐意帮忙。
保持依赖精简,项目越轻快越好。别啥都npm install,有时候自己写个几十行的工具函数,比引入一个几千行的库更靠谱。依赖多了,不仅体积大,安全风险也高,维护成本更是指数级上升。
哪天npm要是挂了(虽然概率极低,但2022年确实出过故障),记得本地还有缓存,node_modules别删,项目还能跑。实在不行,用yarn或pnpm应急,它们的仓库是独立的。
总之,npm是前端开发的基础设施,搞明白它能让你少走很多弯路。希望这篇指南能帮到你,至少让你下次遇到npm ERR!时,不至于一脸懵逼。有问题欢迎交流,咱们下篇见。

更多推荐
所有评论(0)