多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

npm入门指南:从安装配置到常见报错全解析

npm入门指南:从安装配置到常见报错全解析 如果你刚接触前端开发或Node.js生态迟早会在终端里撞见一个红色报错内容大致是“npm : 无法加载文件 ...npm.ps1,因为在此系统上禁止运行脚本”。这时候你才会真正意识到npm 这个天天出现在教程里的名词自己其实只知道用法却从来没搞懂过它到底是什么、为什么要用它、为什么动不动就报错。这篇文章就是写给这样的新人我会从 npm 的全称和底层职责讲起带你把安装、配置、常用命令、镜像源、常见报错全部过一遍顺便聊聊package.json、锁文件、版本号这些绕不开的概念。不涉及高级框架就聚焦于把 npm 这件事讲透看完你可以直接动手干活。1. 先搞清楚npm到底是什么——它解决的其实是一个重复造轮子的问题1.1 一个真实的崩溃现场npm、Node.js和package.json是怎么凑在一起的很多新人第一次接触 npm是因为要先装 Node.js。装完之后在终端里敲一句npm -v输出了一个版本号然后教程里说接下来运行 npm install你就照做了。但很少有人停下来问一句npm 和 Node.js 到底是什么关系简单说Node.js 是一个 JavaScript 运行时环境让 JS 代码可以脱离浏览器在操作系统上直接运行有点像给 JS 装了一台发动机。而 npm 的全称是 Node Package Manager翻译过来就是 Node 包管理器它是随 Node.js 一起分发的一个命令行工具。你可以把 npm 想象成一个应用商店只不过商店里装的不是手机 App而是代码包package也叫依赖dependency。那 package.json 又是什么它相当于项目的清单和配置单。你在项目根目录运行npm init之后会生成这个文件里面记录了项目名称、版本、作者、入口文件、脚本命令以及项目依赖了哪些第三方包、各个包的版本范围。npm 之所以知道你该装什么、装哪个版本靠的全是这个文件。没有 package.jsonnpm 就像没有购物清单去逛超市连买什么都不知道。1.2 npm的三大组成部分注册表、客户端软件和本地仓库npm 能够正常工作背后依赖三个关键部分。第一个是远程注册表registry这是存放所有公开 JavaScript 包的大型数据库网站最常用的就是 npm 官方源 https://registry.npmjs.org/。当你在终端执行npm install时npm 客户端会向这个注册表发起请求查询你需要的包和版本然后下载压缩包回来。第二个组成部分是命令行客户端也就是你安装 Node.js 时顺带装上的 npm 命令。它负责解析 package.json、连接注册表、下载依赖、处理版本冲突、执行脚本等所有交互动作。第三部分是本地仓库具体来说就是你项目里的 node_modules 文件夹和本地的缓存目录。node_modules 里放着所有已经安装的依赖文件而缓存目录npm cache则保存着下载过的压缩包以便下次安装时加速。整个流程可以通俗地理解成你在 package.json 里写下我想要 A 包npm 客户端跑到注册表里找到 A 包同时发现 A 还依赖 B 和 C于是把 A、B、C 一起下载下来解压放进 node_modules最后在项目里生成一份 package-lock.json 来锁定各个依赖的精确版本。下次再有人克隆这个项目只需要运行一次npm install就能得到完全一致的依赖环境。1.3 为什么要用npm不用它行不行我遇见过不少从纯 HTML/CSS/JavaScript 入门的朋友一开始都不理解为什么要多此一举。他们最常问的问题是不就是引用一个 JS 文件吗我把文件下载下来用script标签引入不就行了在只有三五个页面的简单项目里这样做确实可行我早期也这么干过。但当项目规模变大你需要的库可能有十几个、几十个每个库又依赖其他库手动去官网挨个下载、整理版本、处理依赖关系很快就会变成一场灾难。更麻烦的是如果两个库依赖同一个第三方库的不同版本你根本没法在页面里同时引入两个冲突的版本。npm 的意义就在于把这个过程自动化了。你用一行npm install lodashnpm 会把 lodash 以及它依赖的一切都处理好存放在统一的 node_modules 目录里版本冲突交给它的依赖解析机制去协调。你的代码里只需要写const _ require(lodash)或者import _ from lodash就行。这样你完全不用关心下载过程、文件位置、依赖树这些琐碎细节可以把精力集中在真正要写的逻辑上。对于前端构建工具、Node.js 框架、各种命令行工具来说npm 基本成了基础设施一样的存在。2. 新手要过的第一关环境搭建、核心命令与镜像源配置2.1 安装Node.js时最常见的两个选择困境安装 Node.js 本身很简单去官网 https://nodejs.org 下载安装包一路 Next 就行。但新手常卡在两个地方一是 LTS 版本和 Current 版本选哪个二是安装完成后怎么验证成功。LTSLong Term Support是长期维护版本稳定性优先适合绝大多数生产环境和学习场景Current 版本会更快引入新特性但可能存在 API 变动或维护周期短的问题。如果你不是要尝鲜新语法或者做 Node.js 核心开发我建议直接选 LTS省心。装完之后打开终端Windows 是 PowerShell 或 CMDmacOS/Linux 是 Terminal运行node -v能看到 Node 版本运行npm -v能看到 npm 版本两个命令都能正常输出就说明安装基本成功。有个细节值得注意npm 是随 Node.js 一起安装的不需要单独装。所以你搜npm 安装教程时看到的下载 Node.js 就自带 npm这个说法是真的。另外Windows 安装时建议保持默认安装路径不要为了图方便装在带中文或空格的目录里否则后续可能出现环境变量相关的诡异报错。2.2 高频命令速查init、install、run、uninstall 到底怎么用安装好环境之后你需要掌握的核心命令其实没几个。我按实际使用频率从高到低排一下npm init初始化项目生成 package.json。新人可以直接用npm init -y它会跳过提问环节按默认值生成一份可用的清单。npm install 包名安装指定包。后面加--save在 npm 5 之后已经是默认行为不需要手动写了。安装完会写入 dependencies。npm install不带包名时会按 package.json 里记录的所有依赖批量安装。npm uninstall 包名卸载指定包同时自动从 package.json 里移除记录。npm run 脚本名执行 package.json 的 scripts 字段里定义的脚本命令。比如npm run dev通常用来启动开发服务器npm run build用来打包构建。拿一个实际场景来说。你接了一个新项目项目里有 package.json但没有 node_modules 文件夹。你要做的第一件事就是在项目根目录执行npm install把所有依赖装好。装完之后你会发现多了一个 node_modules 目录和一个 package-lock.json 文件。node_modules 体积通常很大可能几百MB甚至更多所以一般会被写进 .gitignore不会提交到 Git 仓库。这也是为什么别人克隆你的代码之后第一件事永远是运行 npm install。2.3 解决下载像蜗牛的千古难题npm镜像源的配置方法新手装上 npm 后第一次执行npm install大概率会被下载速度吓到。原因很简单npm 默认从官方源 https://registry.npmjs.org/ 拉取数据这个源架设在国外国内直连速度不稳定加上某些依赖包体积不小整体体验就很差。好在国内有多个镜像源可用最常用的就是淘宝镜像 https://registry.npmmirror.com。它同步官方源的数据更新频率很快国内访问速度稳定。配置方式有两种一种是一次性参数运行npm install --registryhttps://registry.npmmirror.com只对当前命令生效另一种是永久生效运行npm config set registry https://registry.npmmirror.com之后所有 npm 下载都会走这个源。想确认当前源地址运行npm config get registry就行。如果以后想切回官方源把 set 命令里的地址换回 https://registry.npmjs.org/ 再执行一次即可。另外还有不少老手习惯用 nrm 这个工具来快速切换不同镜像源它本身也是一个 npm 包安装命令是npm install -g nrm。不过对新人来说直接 set 永久配置就够了简单直接不用额外引入工具。这里想多提醒一句公共镜像确实方便但如果你发布自己的 npm 包切回官方源再跑发布命令通常更稳妥避免某些镜像同步延迟造成版本状态不一致。3. 高频报错排查实录那些让新手心态崩掉的经典问题3.1 PowerShell执行策略npm.ps1无法加载的解决办法文章开头提到的那个报错几乎每个 Windows 新手都会遇到。完整提示是npm : 无法加载文件 C:\Program Files\nodejs\npm.ps1因为在此系统上禁止运行脚本。看到这段英文和中文混合的报错时大家第一反应是 npm 坏了实际上 npm 好好的问题出在 PowerShell 的执行策略上。PowerShell 出于安全考虑默认禁止运行脚本文件.ps1而 npm 在 PowerShell 里是通过 npm.ps1 这个脚本去执行的所以被拦住了。解决办法是在 PowerShell 里修改执行策略以管理员身份打开 PowerShell运行Set-ExecutionPolicy RemoteSigned然后输入 Y 确认。这个策略的意思是本地创建的脚本可以运行从互联网下载的脚本必须经过签名。改完重新打开终端npm 命令就能正常执行了。如果不想修改全局策略也有一个更温和的方案只对当前用户生效运行Set-ExecutionPolicy -Scope CurrentUser RemoteSigned。这样不会影响系统其他用户和配置我个人更推荐这种做法。实际上我自己现在在 Windows 的终端配置里就是这么处理的既解决了脚本执行问题又不需要管理员权限。3.2 npm不是内部或外部命令环境变量排查指南另一个高频报错是npm 不是内部或外部命令也不是可运行的程序或批处理文件。这个问题通常出现在两种情况下一是 Node.js 安装时没把 npm 路径写进系统环境变量二是你卸载了 Node.js 但环境变量还残留着指向旧路径。在前面的热搜词里你可以看到类似npm环境变量path配置、npm : 无法将npm项识别为 cmdlet...这些搜索都跟这个报错相关。排查思路是先找到 npm 的安装目录比如C:\Program Files\nodejs\或D:\Program Files\nodejs\在里面能看到 npm、npm.cmd 这几个文件确认存在后把这个目录加到系统环境变量 Path 里。具体操作是右键此电脑→ 属性 → 高级系统设置 → 环境变量在系统变量里找到 Path编辑并新增一行填入你的 Node.js 安装目录。保存后重新打开一个终端窗口环境变量修改后旧的终端窗口不会自动刷新再敲npm -v验证。如果你用的是 mac 或 Linux一般不会出现这种问题因为 Node.js 的安装包会自动把可执行文件链接到 /usr/local/bin 目录该目录默认已在 PATH 里。这里补一句实操心得环境变量这东西修改后一定记得重开终端这是新手最容易忽略的一步。我在教学时见过有人改了路径但一直在旧窗口验证反复报错半天最后才发现是没重开终端的问题。3.3 那些让人心慌的警告deprecated 和 peer dependency 到底要不要管很多新人在安装 npm 包时会看到终端里刷出npm WARN deprecated node-domexception1.0.0: use your platforms native dome之类的黄色警告第一反应是我是不是装错了什么。实际上 deprecated 警告的含义是某个依赖包发布者已经标记这个包或这个版本过时了建议用替代方案。它不代表安装失败也不代表你的项目有问题很多时候是你用的某个工具间接依赖了这个过时包。那要不要管我的建议是先看看警告来自哪个包如果只是间接依赖warning 信息里会显示 node_modules/xxx 这样的路径可以暂时不处理但如果警告出现在你自己直接安装的包上比如某个 CLI 工具提示过时那就要留意了可能功能会有兼容性问题。这类警告真正需要担心的场景是安全漏洞如果你装的是知名框架或工具可以关注一下官方是否发布了替代版本。还有一个高频警告是npm WARN ERESOLVE overriding peer dependency。peer dependency 的意思是我这个包需要和指定版本的另一个包配合使用有点像插件和主程序的关系。当项目里某个依赖要求的 peerDependencies 与当前安装的版本冲突时npm 7 之后不再像以前那样默默兼容而是会报一个 ERESOLVE 错误并尝试覆盖处理。遇到这种情况时不要慌也不需要删除重装通常是你显式安装一个满足要求的版本或者在 install 时加上--legacy-peer-deps让 npm 按旧版的宽松策略处理。不过 --legacy-peer-deps 属于妥协方案能跑通项目但不是最佳路径长期来看还是要解决实际版本冲突。3.4 node_modules坏了清缓存重装的正确姿势在实际开发中你还会遇到一种特别磨人的场景代码没改依赖没动但运行npm run dev时开始报各种奇怪的错误比如模块找不到、版本对不上、类型错误。这种情况八成是 node_modules 目录里的依赖状态出了问题原因可能是不完整的安装、突然断电中断了写入、或者你手动改过 node_modules 里的文件。处理方式其实很简单就是删了重新装。先删除 node_modules 目录Windows 下可以直接删或者用命令rmdir /s /q node_modules再删除 package-lock.json这一步要看情况如果你确认 package.json 没有变动lock 文件可以保留然后运行npm install。如果还是有问题再执行npm cache clean --force清一下 npm 缓存然后重新 install。这个操作听起来粗暴但确实能解决绝大部分依赖损坏的问题。我在实际项目里见过太多人绕来绕去找配置问题最后我让删 node_modules 重装一次就好。这里有一个小技巧项目比较大的时候rm -rf node_modules npm install要等很久有点浪费时间。所以后来我发现npm ci这个命令在按 lock 文件完全重建依赖的场景下更好用后面会专门讲。4. 理解版本号的秘密从乱装依赖到看懂依赖管理4.1 语义化版本号^、~ 和精确版本到底怎么选如果你打开 package.json会看到 dependencies 里的内容长这样{ dependencies: { lodash: ^4.17.21, react: ~18.2.0, vue: 3.4.21 } }这些版本号并不是随便写的它们遵循语义化版本规范Semantic Versioning缩写 semver格式是主版本号.次版本号.补丁号。举个例子4.17.21 里 4 是主版本17 是次版本21 是补丁。主版本号变化表示不兼容的 API 变更次版本号变化表示向后兼容的新功能补丁号变化表示向后兼容的问题修复。版本号前面的符号决定了 npm 在安装时允许的版本范围。^4.17.21表示允许安装 4.x.x 系列中大于等于 4.17.21 的最新版本但不能升到 5.0.0因为主版本号变化可能带来不兼容。~18.2.0表示只允许 18.2.x 范围内的补丁升级比如 18.2.1、18.2.5但不能到 18.3.0。不带符号的3.4.21则是精确版本只装这一个。4.2 package-lock.json为什么它是项目的定海神针新人在刚接触项目时常会发现除了 package.json 还有一个 package-lock.json体积通常比 package.json 大不少。它的作用很简单锁定整棵依赖树中每一个包的精确版本号、下载地址和校验值。package.json 里写的版本是范围而 lock 文件里记录的是最终装的那个确切版本。为什么需要它因为如果两个人在不同时间执行npm installpackage.json 里的范围可能解析出不同的实际版本。比如项目 A 用了^4.17.21一个人在上周装到了 4.17.23另一个同事今天装的时候可能解析到 4.17.28两人环境不一致代码行为就可能出现微妙差异。有了 lock 文件后npm install 会优先按 lock 文件记录的精确版本安装保证每个人本地环境一致。我自己开发中最深的体会是lock 文件一定要提交到 Git 仓库不要加进 .gitignore。很多新手觉得它是自动生成的不用管结果忽略了它导致团队环境千差万别。对于应用型项目lock 文件是团队依赖一致性的核心保障对于库项目你要发布的 npm 包是否提交 lock 文件有争议但应用项目请务必提交。4.3 npm install 还是 npm ci不同场景下的选择npm install和npm ci是新手很容易混淆的两个命令它们都能安装依赖但行为有重要区别。npm install会读取 package.json并在安装过程中更新 lock 文件可能升级依赖到允许范围内的最新版同时会检查并修改依赖树。而npm ci会严格按 package-lock.json 里的记录安装安装前会先删除 node_modules不做任何版本浮动和解析也不会改动 lock 文件速度往往更快。所以场景就很清楚了日常开发时想新增一个包用npm install 包名在 CI/CD 流水线、部署环境或者需要完全复现他人环境时用npm ci。还有前面提到的依赖损坏、神奇报错场景也可以优先试npm ci因为它的清空重装特性和 lock 文件严格绑定能最大程度排除环境不一致造成的问题。我改掉旧习惯的一个关键节点是有一次部署到测试环境同事说本地测试没问题但线上挂了排查半天发现就是因为我本地 package-lock.json 更新过而线上是用 npm install 按 package.json 重新解析的两边其实装出了不同的版本树。从那以后部署和流水线我基本只用 npm ci。5. 从使用者到发布者把你的第一个包推到 npm 上5.1 发布前必须做的准备包信息、账号和命名规则用 npm 装了一堆别人的包之后你可能也会冒出我能不能也发布一个包的念头。答案是当然可以而且流程比想象中简单。发布前要做三件事准备一个待发布的包、注册 npm 账号、在本地登录账号。包本身怎么准备你需要一个文件夹里面有 package.json。npm init -y会生成一份默认配置但发布前要检查几个关键字段name 是包名必须全局唯一不能和别人已发布的包重名npm 官方源对命名也有规范比如不能有大写字母、不能以点或下划线开头version 是版本号按语义化版本写main 字段指定包的入口文件也就是别人 require 或 import 你的包时加载的那个 JS 文件如果包里有可执行的命令行工具还需要配置 bin 字段。注册账号直接在 https://www.npmjs.com 上注册就行注册完不会自动登录到终端需要在终端里运行npm login输入用户名、密码、邮箱完成后你的本机就有了发布凭据。5.2 一个最小可用的发布流程从创建到发布我用一个最简单的例子演示。先创建一个文件夹mkdir my-first-package cd my-first-package npm init -y然后编辑 package.json把 name 改成你喜欢的唯一名字比如my-demo-tool-2024这个名字基本不会有人用但也要先在 npm 上搜一下确认。接着创建一个 index.jsfunction hello(name) { return Hello, ${name}! } module.exports hello最后在终端里运行npm publish。如果一切顺利几秒后你就拥有了自己发布的第一个 npm 包。别人可以通过npm install my-demo-tool-2024来安装并引用它。需要注意的是npm publish默认会把当前目录下所有文件都打进去除非有 .npmignore 或 package.json 里的 files 字段来限制。新手容易把 node_modules、测试文件、本地配置文件一并发布出去显得很业余。建议在 package.json 里加一个 files 字段显式声明要包含哪些文件比如{ files: [index.js, README.md] }5.3 发布后的维护版本更新和失误补救发布过一次之后后续维护就是围绕版本迭代展开。修改代码后要更新 package.json 里的 version 字段再运行npm publish。版本号更新可以手动改也可以运行npm version patch补丁1、npm version minor次版本1、npm version major主版本1。npm version 会自动更新 package.json 并打一个 Git tag推荐使用。如果发布失误比如传了包含敏感信息的包npm 允许你删掉它运行npm unpublish 包名 --force。但有严格限制只有在发布后 72 小时内才能删除包且如果一个包有多于 72 小时的发布历史就无法整体删除。所以在发布前多做本地验证绝对值得。发布这件事我个人的体会是别一上来就想搞大而全的框架先发一个解决小问题、单文件、纯函数式的包走一遍从 init 到 publish 的完整流程理解每个环节的意义。有了第一次之后后面再接触 monorepo、scope 包、私有包都会顺理成章。6. 写在最后给新手的几个实用建议文章写到这里该讲的技术点都讲得差不多了最后想以过来人的身份给你分享几个实际体会。第一个建议是别害怕报错。npm 的报错信息虽然看起来吓人但绝大多数都能通过搜索报错原文找到答案。你自己排查报错的过程其实就是理解 npm 工作方式的过程。我在前面列的几个高频问题——ps1 执行策略、环境变量、deprecated 警告、node_modules 损坏——几乎覆盖了新手日常会遇到的大部分坑。第二个建议是养成看官方文档的习惯。npm 的官方文档写得非常详细尤其是 package.json 配置说明和 CLI 命令手册几乎所有疑问都能在里面找到权威答案。比在 CSDN 上看各种二手教程舒服多了也更可靠。第三个建议是项目里尽量保持依赖干净。不用什么功能都往 dependencies 里塞能用项目自带能力解决的就别引入第三方包每次添加依赖前想清楚它是否被维护、是否足够流行、是否有替代方案。一个 dependencies 列表膨胀过度的项目最终维护起来会非常痛苦。最后再分享一个小技巧如果你发现自己经常在初始化一些固定项目结构可以把一套基础依赖和脚本配置整理成自己的脚手架包发布到 npm 上以后新项目直接用npm init配合自定义初始化器一秒搞定。npm 能做的事远远不止装包这么简单越用深入你会越发现它作为整个 JavaScript 生态基础设施的真正价值。
返回列表