npm、npx、Node.js 到底是什么?把终端里那些相似命令一次讲清

刚开始跟着 AI 写代码,最容易遇到的障碍,往往是一串看起来差不多的命令。

上一句是 npm install,下一句变成 npx create-vite,接着又是 npm run dev。教程还让你安装 Node.js、提交 lock 文件,却提醒你别提交 node_modules。

它们都跟 JavaScript 项目有关,但各自负责不同的事。理解这些分工后,你就能看懂 AI 准备做什么,也更容易找到报错发生在哪一步。

本文于 2026-10-19 核对 Node.js 官方资料及 npm CLI v11 文档。命令选项、下载确认和安装脚本策略可能随版本变化;示例用于解释行为,不代表已经在你的电脑上执行。

一、先认识这几位:谁负责运行,谁负责安装

名称 负责什么 可以怎样理解
JavaScript 编程语言 写程序使用的语言
Node.js 在浏览器之外运行 JavaScript 的运行时 执行程序的环境
npm 管理依赖、运行脚本、发布包的命令行工具,也对应 npm 包生态与服务 项目的工具管理员
npx 执行 npm 包提供的命令;现代版本基于 npm exec 找到一个工具并运行
npm registry 保存和分发包的注册表服务 下载包的来源

浏览器能运行 JavaScript,Node.js 也能,但它们提供的环境不同。浏览器有网页 DOM,Node.js 则能提供文件系统等能力。安装 Node.js,并不等于安装了一个浏览器。

常见的 Node.js 安装方式通常会一起提供 npm 和 npx;具体取决于安装渠道。可以用下面三条命令检查当前终端能找到什么:

node --version
npm --version
npx --version

这是三个命令的版本,不要求版本号一致。若提示 command not found,先检查安装与 PATH,也就是终端寻找可执行程序的路径,别急着修改项目源码。

二、包、依赖、命令:为什么名字不一定一样

包(package) 是发布和安装的单位。项目需要某个包,就把它列为 依赖(dependency)。

包里可以有供程序调用的代码,也可以提供终端命令,还可以两者都有。

例如:

  • lodash 常用于代码中的数据处理,不意味着安装后就有一个日常可用的 lodash 终端命令。
  • typescript 是包名,它提供的编译命令叫 tsc。
  • vite 既是包名,也提供同名命令。

所以,“装了一个包”和“得到一个同名命令”不能画等号。包通过 package.json 的 bin 字段声明可执行入口。

这也是下面这条命令容易写错的原因:

npx --package typescript tsc --version

这里的意思是:“使用 typescript 这个包提供的环境,执行 tsc --version。”不要因为命令叫 tsc,就随手把 npx tsc 当成指定 TypeScript 包的等价写法;npm 上的同名包不一定是你想要的工具。

三、npm install、npm run、npx:三种动作

1. npm install:把依赖准备好

在已有项目目录执行:

npm install

npm 会结合 package.json 和已有 lock 文件解析并安装依赖。lock 文件与声明相符时可以使用锁定版本;不相符时,npm install 可能更新 lock 文件。

如果要给项目添加包,则指定包名:

npm install lodash
npm install -D typescript

普通安装默认保存为 dependencies;-D 是 --save-dev,保存为 devDependencies,常用于构建、检查、测试等开发工具。

这里有个容易误会的地方:devDependency 不等于“部署时绝对用不到”。 如果服务器要现场构建前端,构建步骤仍可能需要 Vite 或 TypeScript。关键是部署流程在哪里构建、运行时需要什么。

2. npm run:执行这个项目定义的脚本

假设 package.json 里写着:

{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  }
}

那么:

npm run dev
npm run build
npm run preview

分别执行对应的脚本。dev、build、preview 是项目作者起的名字,含义要看实际内容。

不是每个项目都有 npm run dev,也不是每个 build 都在做同一件事。 不带脚本名运行 npm run,可以查看当前项目列出的脚本。

npm 运行脚本时会把本地工具目录 node_modules/.bin 加入命令搜索路径。因此脚本里直接写 vite,通常就能找到项目安装的 Vite,无需全局安装。

给脚本传参数时,常见写法是:

npm run dev -- --port 5173

前面的 -- 用来把后续参数交给脚本中的命令。这里传给的是 Vite;它是否支持该选项,要查工具本身的说明。

3. npx:找到工具并执行

npx create-vite@latest

这类写法适合运行项目生成器,或偶尔使用的命令行工具。

现代 npx 基于 npm exec。请求的包如果能匹配项目已有依赖,就可以使用本地版本;如果本地没有匹配的包,则可能下载到 npm 缓存目录,再把工具加入此次执行环境。

npx 并不是“什么都不安装”,而是通常不把临时工具加入你的项目依赖或全局安装目录。 缓存也不等于运行后立即删除,下次可能复用。

需要下载时,交互终端通常会询问;选项、非交互环境和版本会影响确认行为。-y / --yes 可以跳过下载确认,但它不会让包变得更可信。

下面两种命令的目的相近:

npx eslint .
npm exec -- eslint .

它们的参数解析有区别,不能对复杂命令只做机械替换。需要明确指定包和命令时,可以这样写:

npm exec --package typescript -- tsc --version

还要注意:@latest 是包发布者维护的 dist-tag,表示该标签当前指向的版本。它方便尝鲜,但下个月运行可能得到不同版本。正式协作时,应结合明确版本、项目依赖和 lock 文件管理工具版本。

四、三个文件和目录:清单、安装结果、版本记录

打开一个常见的 npm 项目,你会看到:

my-project/
├── package.json
├── package-lock.json
└── node_modules/
文件或目录 保存什么 一般是否提交 Git
package.json 项目元信息、脚本、依赖与版本要求 是
package-lock.json 解析后的依赖树、具体版本及相关元数据 npm 项目通常提交
node_modules/ 安装后实际使用的依赖文件 通常不提交

例如,package.json 可以声明一个允许更新的版本范围,lock 文件记录当前解析出的具体结果。

有了 lock 文件,团队和部署环境就更容易安装一致的依赖。不过,依赖树一致也不等于整个运行环境完全相同:Node.js 版本、操作系统、CPU 架构、安装选项和外部服务依然可能影响结果。

这也解释了为什么 node_modules 通常不进 Git:依赖应通过声明和 lock 文件重建,不需要把整个安装目录搬进仓库。

五、npm install 和 npm ci:什么时候选哪个

开发中新增依赖,用 npm install 很自然。部署和持续集成中,更常见的是:

npm ci

它要求已有 package-lock.json 或 npm 支持的 shrinkwrap 文件,并且与 package.json 的依赖声明一致;不一致时会报错,而不是帮你改好。

它还会先移除已有 node_modules,再进行干净安装,并且不会改写 package.json 或 lock 文件。

你的目的 通常选什么
第一次准备一个尚未生成 lock 的项目 npm install
添加或调整项目依赖 npm install 包名
根据已提交 lock 做干净安装 npm ci
lock 与声明不一致,正在处理依赖变更 查明原因,用 npm install 更新并评审差异

如果生成 lock 时使用了影响依赖树的选项,npm ci 也需要兼容的配置。不要把 CI 失败后的第一反应变成“删除 lock 再试”;那可能顺带改变大量依赖版本。

六、全局、本地、临时执行:选谁才合适

# 安装到当前项目,作为开发依赖
npm install -D typescript

# 全局安装,安装位置由 npm 配置决定
npm install -g typescript

# 为这次命令找到并使用 TypeScript 包
npx --package typescript tsc --version

全局安装的工具适合个人经常使用的 CLI,但“全局”不等于整台电脑所有用户、所有 Node.js 环境共用同一份。换一个 Node.js 安装或版本管理环境,工具位置与可见性可能变化;安装了也可能因为 PATH 没配好而找不到。

对于项目构建工具,优先使用项目本地依赖和脚本,方便团队共享版本。对于一次性的项目生成器,npx 很方便。对于每天都用的个人工具,再考虑全局安装。

你还会见到 pnpm、Yarn、Bun。pnpm 和 Yarn 也能管理 JavaScript 依赖、运行脚本;Bun 同时提供运行时、包管理器等工具。它们有相似功能,但版本、选项和 lock 格式各有差异。

进入已有项目时,先看 README、package.json 中的 packageManager 字段(若存在),以及 lock 文件。 看到 pnpm-lock.yaml 或 yarn.lock,先按项目约定操作,不要立刻运行 npm 并额外生成第二种 lock 文件。多个 lock 并存时,查清项目真正使用的工具。

七、把一个项目从零跑起来,命令就串起来了

下面是一个 Vite 项目示例,假设你愿意下载并执行官方 create-vite 工具;具体交互和 Node.js 要求以当前工具为准:

# 生成项目文件,选择框架和模板
npx create-vite@latest my-demo

# 进入生成的项目目录
cd my-demo

# 如果生成器尚未安装依赖,再执行这一步
npm install

# 启动开发服务
npm run dev

第一步运行生成器;第二步切换目录;第三步准备依赖;第四步执行项目脚本。它们做的是四件事。

生成器可能询问是否立即安装或启动,如果已经完成,就不必机械重复。开发服务启动后,打开终端实际输出的地址;端口被占用时,工具可能选择其他端口。

准备生产发布时,通常先执行 npm run build,再部署生成的文件。npm run preview 在这个示例里用于本地预览构建结果,不代表网站已经上线,也不应直接当作生产服务方案。

最后,把常见报错放回对应环节:

现象 先检查什么
node / npm 找不到 安装、当前 shell 与 PATH
找不到 package.json 是否进入了正确的项目目录
Missing script: dev package.json 是否真的定义了 dev
安装依赖时失败 日志里的 registry、网络、版本与依赖冲突信息
npm ci 提示 lock 不一致 声明与 lock 是否同步,安装配置是否一致
npx 不能确定要运行哪个命令 包是否提供可执行入口,包名与命令名是否不同

看懂命令的用途后,再让 AI 排障,问题描述就会具体得多:“依赖还没安装成功”,和“依赖已安装,但 dev 脚本启动失败”,需要检查的东西并不相同。

官方资料