前端开发绕不开: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会做这几件事:

  1. 检查package.json,看看要装哪些包
  2. 解析版本范围,确定具体装哪个版本
  3. 下载包文件,放到node_modules
  4. 更新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.0
  • 2.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会:

  1. 根据package.json里的版本范围,确定具体版本
  2. 把这个具体版本写进package-lock.json
  3. 以后所有人安装,都按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  # 安装指定预发布版

为啥别人拉代码跑不起来,大概率是版本问题

经典场景:

  1. 你项目用了Node 16,同事电脑是Node 12
  2. 某个包在Node 12上不兼容
  3. 同事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可能会让项目崩掉。

正确姿势:

  1. 先读官方迁移指南(Migration Guide)
  2. 在分支上升级,测试通过再合并
  3. 使用工具检查兼容性,比如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包提供命令行工具,比如webpackeslinttsc。它们是怎么注册为命令的?靠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!时,不至于一脸懵逼。有问题欢迎交流,咱们下篇见。

在这里插入图片描述

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐