npm:package.json 详解

package.json 是 Node.js 项目和 npm 包的清单:写清身份、入口、脚本和依赖。它必须是 纯 JSON(不能是带注释的 JavaScript 对象字面量)。npm install 根据它和 lockfile 装依赖。

不打算发布的应用也可以没有严格的 nameversion;要发布到 registry,这两个字段是必填的,二者合起来构成包的唯一标识。

创建 package.json

有两种方式:

  • 手动创建:在项目根目录新建 package.json,写入 JSON。
  • 自动创建:在项目根目录执行 npm init,按提示填写;npm init -y(或 --yes)用默认值直接生成。

一个简单示例:

{
  "name": "project-name",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

package.json 配置说明

name

包名称。要发布则必填,否则可选。

规则:

  • 长度不超过 214 个字符(含范围包的 scope,如 @babel/core 里的 @babel);
  • 范围包的名称可以以 ._ 开头,非范围包不行;
  • 新包名称 不得包含大写字母
  • 名称会进入 URL、命令行参数和文件夹名,因此不能包含非 URL 安全字符。

不要和 Node.js 核心模块重名。范围包写作 @scope/name

version

包版本号。要发布则必填,否则可选。必须是 node-semver 能解析的字符串。

标准版本号是 X.Y.Z,均为非负整数,禁止数字前方补零:

  • X 主版本号(major):不向后兼容的重大变更。
  • Y 次版本号(minor):向后兼容的功能增加。
  • Z 修订号(patch):向后兼容的缺陷修复。

不稳定、尚未承诺兼容时,在标准版本号后用 - 接预发布标识,标识之间用 . 分隔(可再加 + 编译信息):

  • 内部版本(alpha),例如 2.0.0-alpha.1+8ad8as.20220129
  • 公测版本(beta),例如 2.0.0-beta.1
  • 正式版候选(rc,Release Candidate),例如 2.0.0-rc.1

查看 registry 上某包的版本:

npm view <package-name> version
npm view <package-name> versions

description

一段描述,字符串。便于用户理解,也参与 npm search

keywords

关键字,字符串数组。同样便于搜索发现。

homepage

项目主页 URL。npm docs 会打开文档、主页(不是仓库)。

{
  "homepage": "https://github.com/owner/project#readme"
}

bugs

缺陷跟踪的 URL 和(或)报告问题的邮箱。

{
  "bugs": {
    "url": "https://github.com/owner/project/issues",
    "email": "project@hostname.com"
  }
}

可以只给其中一项。只提供 URL 时,值可以直接是字符串:

{
  "bugs": "https://github.com/owner/project/issues"
}

在包目录执行 npm bugs 会在浏览器打开该 URL;没有 URL 则打开该包在 npmjs.com 上的页面。

license

许可证,让使用者知道权利和限制。常用 SPDX 标识:https://spdx.org/licenses/。多许可证可用 SPDX 表达式,例如 (ISC OR MIT)

{
  "license": "ISC"
}

ISC:允许出于任何目的使用、复制、修改和分发,是否收费均可,条件是副本中保留版权声明和本许可声明。

{
  "license": "UNLICENSED"
}

UNLICENSED(注意拼写,不是 Unlicensed):不在任何条款下授权他人使用该私有或未发布包。自定义正文可用 "SEE LICENSE IN <filename>"

旧的 license 对象、licenses 数组写法已废弃,改用 SPDX 字符串。

author、contributors

author 是一个人,contributors 是一组人。字段结构相同:author 是对象,contributors 是对象数组。emailurl 可选。

{
  "name": "Barney Rubble",
  "email": "b@rubble.com",
  "url": "http://barnyrubble.tumblr.com/"
}

也可写成一行:

{
  "author": "Barney Rubble <b@rubble.com> (http://barnyrubble.tumblr.com/)"
}

funding

包的资助方式:对象、URL 字符串或数组。

{
  "funding": {
    "type": "individual",
    "url": "http://example.com/donate"
  }
}
{
  "funding": "http://example.com/donate"
}
{
  "funding": [
    {
      "type": "individual",
      "url": "http://example.com/donate"
    },
    "http://example.com/donateAlso",
    {
      "type": "patreon",
      "url": "https://www.patreon.com/my-account"
    }
  ]
}

npm fund 列出当前项目依赖树里的资助信息;npm fund <package-name> 看某一个包(多个 URL 时列出第一个)。

files

作为依赖安装时,tarball 要包含 哪些路径:文件、目录或 glob(***/* 等)。语法像 .gitignore,含义相反:这里是白名单。省略该字段时,效果等同 ["*"](再减去忽略规则),即默认打包几乎全部文件。

也可在根目录或子目录放 .npmignore

  • 没有 .npmignore 时,才用 .gitignore 当忽略清单。
  • 两者都有 时,打包以 .npmignore 为准,不是「有 .gitignore 就忽略 .npmignore」。
  • 根目录的 .npmignore 不会覆盖 files 白名单;子目录里的 .npmignore 仍会排除该子树中、即使已被 files 选中的文件。

根级 files 列出的文件,不能靠根目录的 .npmignore.gitignore 再排除掉。

无论怎样配置,下列文件 始终打包package.jsonREADMELICENSELICENCEmain 指定的文件、bin 指定的文件。READMELICENSE 的大小写和扩展名不限。

下列路径 默认不进包(其中一部分即使写进 files 也带不走):.gitCVS.svn.hg.lock-wscript.wafpickle-N.*.swp.DS_Store._*npm-debug.log.npmrcnode_modulesconfig.gypi*.origpackage-lock.jsonpnpm-lock.yamlyarn.lockbun.lockb

.git.npmrcnode_modules 以及各类 lockfile 无法files 强行打进去。若发布时需要锁版本,用 npm-shrinkwrap.json 代替 package-lock.json

main

CommonJS 主入口。包名叫 project-name 时,require("project-name") 得到该文件的 exports。路径相对包根目录,例如 dist/index.js。未设置则默认根目录 index.js

type

告诉 Node.js 如何解释本包里的 .js 文件。npm 自己不用这个字段。

  • "commonjs"(默认):.js 当 CommonJS。
  • "module".js 当 ESM。.cjs.mjs 仍按后缀强制。

exports

现行主入口字段,用来代替只写一个 main。可以声明多个入口、按 importrequirenodedefault 等条件导出,并且 没写进 exports 的路径默认不能从包外引用

{
  "type": "module",
  "main": "./dist/index.cjs",
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "require": "./dist/index.cjs"
    },
    "./package.json": "./package.json"
  }
}

同时写了 exportsmain 时,Node.js 解析入口以 exports 为准。细节见 Node.js 的 包入口

imports

内部 的子路径映射,键必须以 # 开头,只在本包代码里生效,不会开放给使用者。

{
  "imports": {
    "#utils": "./src/utils.js"
  }
}

browser

类似 main,但面向客户端(浏览器)。打包工具会读它。写在这里等于提示:这份入口会依赖 window 等 Node.js 里没有的 API。Node.js 运行时本身不按 browser 解析。

bin

.js 可以用 node 执行:

/* bin_test.js */
console.log('abcd');

在该文件所在目录执行 node bin_test.js 会打印 abcd。每次都要拼完整路径,命令行工具就不方便。

bin 把「指令名」映射到可执行文件:

  • 全局安装时,Unix 会链到全局 bin 目录(常见 /usr/local/bin,用了 nvm 则在对应 Node 前缀下);Windows 会在全局前缀生成 .cmd(常见 %AppData%\npm\command-name.cmd)。
  • 作为依赖装进项目时,会在该项目的 node_modules/.bin 生成同名命令。npm runnpx 会自动把这个目录加进 PATH。也可直接跑 ./node_modules/.bin/command-name
{
  "bin": {
    "command-name": "./bin/index"
  }
}

全局安装 npm i my-program -g 之后,终端输入 command-name 即执行该文件。

只有一个可执行文件且命令名等于包名时,可以写成字符串:

{
  "name": "my-program",
  "version": "1.2.5",
  "bin": "./path/to/program"
}

等价于:

{
  "name": "my-program",
  "version": "1.2.5",
  "bin": {
    "my-program": "./path/to/program"
  }
}

Unix 上请保证这些文件首行是 #!/usr/bin/env node,否则系统不会用 Node 解释。Windows 的 .cmd 垫片不依赖 shebang,但跨平台包仍应写上。

#! 在 Unix 里叫 shebang。普通文件以它开头时,内核按后面的解释器执行。# 在不少脚本里是注释,解释器会跳过这一行。

/usr/bin/env node:在 PATH 里找名为 node 的解释器,避免写死 /usr/bin/node

man

man 是 manual 的简写,给 man 程序提供帮助页。值为一个文件或多个文件名组成的数组。

只指定一个文件时,无论文件名是什么,最终命令都是包名:

{
  "name": "my-program",
  "man": "./man/doc.1"
}

实际执行的是 man my-program

文件名不以包名开头时,包名会作为前缀:

{
  "name": "my-program",
  "man": [
    "./man/my-program.1",
    "./man/othername.1"
  ]
}

对应 man my-programman my-program-othername

man 文件必须以数字结尾,压缩后可加 .gz。数字表示安装到 man 的哪一节:

{
  "name": "my-program",
  "man": [
    "./man/my-program.1",
    "./man/my-program.2"
  ]
}

对应 man my-programman 2 my-program

directories

bindoclibman 等标明目录。directories.bindirectories.man 会被 npm 用来展开该目录下的文件;doclib 主要是元数据。

{
  "directories": {
    "bin": "./bin",
    "doc": "./doc",
    "lib": "./lib",
    "man": "./man"
  }
}

不要同时写一个具体的 bin 文件路径和 directories.bin。单个文件用 bin;整目录用 directories.bin

repository

源码仓库地址。GitHub 上的仓库可用 npm repo 打开(现行文档不再把这件事记在 npm docs 上;npm docs 打开的是 homepage、文档)。

{
  "repository": {
    "type": "git",
    "url": "git+https://github.com/npm/cli.git"
  }
}

url 应是可以直接交给 Git 的地址,不要填浏览器里的 HTML 项目页。发布时 npm 会把简写规范化成对象;新包建议直接写对象,避免 publish 警告。

GitHub、gist、Bitbucket、GitLab 也可以用和 npm install 相同的简写(下列是几种写法,不是同一个 JSON 里的重复键):

npm/npm
github:user/repo
gist:11081aaa281
bitbucket:user/repo
gitlab:user/repo

package.json 不在仓库根目录时(monorepo 子包),用 directory 指出所在目录:

{
  "repository": {
    "type": "git",
    "url": "https://github.com/facebook/react.git",
    "directory": "packages/react-dom"
  }
}

scripts

内置生命周期脚本、以及任意自定义脚本。用 npm run-scriptnpm run 执行。名为 preXpostX 的脚本会夹在 X 前后跑(如 precompresscompresspostcompress)。依赖包里的脚本可用 npm explore <pkg> -- npm run <script>

{
  "scripts": {
    "precompress": "echo before",
    "compress": "echo compress",
    "postcompress": "echo after"
  }
}

运行 npm run compress 时,这三个脚本都会执行。

npm startnpm testnpm stopnpm restart 是少数不必写 run 的内置命令,同样走 prepost

只在特定情况下触发的生命周期

  • prepare(npm 4 起)
    • npm publishnpm pack 打包之前;
    • 在包根目录执行 不带包名npm install(可带 --omit=dev 等旗标;npm install express 这种加包名的不会跑);
    • 通过 git URL 安装、且该包声明了 prepare 时:会装它的 dependenciesdevDependencies,并在打包、安装时执行 prepare
    • 从 npm 7 起这些脚本默认在后台跑,要看输出加 --foreground-scripts
  • prepublish:已不推荐。历史原因是 npm install 也会跑它,名字容易误解。新脚本用 prepareprepublishOnly
  • prepublishOnly:只在 npm publish 时、准备打包之前跑。
  • prepack:打 tarball 之前(npm packnpm publish、安装 git 依赖时)。注意 npm run pack 只是你自定义的脚本名,和 CLI 的 npm pack 不是一回事。
  • postpack:生成压缩包之后、挪到最终位置之前(publish 不会把 tarball 留在本地)。

常见命令的生命周期顺序

  • npm cache add、npm diff: prepare
  • npm ci、npm install(无包名): preinstallinstallpostinstallprepublishprepreparepreparepostprepare
  • npm pack: prepackpreparepostpack
  • npm publish: prepublishOnlyprepackpreparepostpackpublishpostpublish
  • npm rebuild: preinstallinstallpostinstallprepare
  • npm start、npm stop、npm test、npm restart: pre<名称><名称>post<名称>
  • npm run \<自定义脚本>: pre<名称><名称>post<名称>

prepublish 出现在 install 流程里,不会出现在 publish 流程里。不要把两条链混成「prepare 夹在 prepublish 和 prepublishOnly 之间」这一句话。

npm 7 起 不再 提供 uninstall 生命周期脚本。

在当前包下调用依赖里的可执行文件

{
  "name": "foo",
  "dependencies": {
    "bar": "0.1.x"
  },
  "scripts": {
    "start": "bar ./test"
  }
}

npm start 会执行 bar ./testnpm install 之后,依赖的 bin 会出现在 node_modules/.binnpm run 能直接叫到命令名。

package.json 变量

package.json 顶层字段会加上 npm_package_ 前缀进入环境变量,嵌套键用 _ 连接:

/* var.js */
console.log(process.env.npm_package_name, process.env.npm_package_version);
console.log(process.env.npm_package_scripts_test);
{
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "var": "node ./var.js"
  }
}

运行 npm run var 查看输出。

退出

脚本交给 sh(Windows 上是 cmd,可用 script-shell 改)。退出码非 0 会中止后续流程。

脚本不必是 Node.js,但必须是某种可执行文件。

config

给包脚本用的一组参数:

{
  "name": "foo",
  "config": {
    "port": "8080"
  }
}

脚本里读环境变量 npm_package_config_port。用户也可用 npm config set foo:port 80 覆盖(foo 是包名)。

dependencies(项目依赖)

当前包运行所需的依赖,是「包名 → 版本范围或 URL」的对象。版本范围遵循 semver,多个范围用空格分隔。

不要把测试、构建、仅开发期用的包装进这里,那些放 devDependencies

指定版本范围

  • version: 完全匹配
  • >version: 大于 version
  • >=version: 大于等于 version
  • <version: 小于 version
  • <=version: 小于等于 version
  • ~version: 若写了次版本,则只允许补丁级变动;若只写到主版本,则允许次版本变动
~1.2.3         等价于 >=1.2.3 <1.3.0-0
~1.2           等价于 >=1.2.0 <1.3.0-0   同 1.2.x
~1             等价于 >=1.0.0 <2.0.0-0   同 1.x
~0.2           等价于 >=0.2.0 <0.3.0-0   同 0.2.x
~1.2.3-beta.2  等价于 >=1.2.3-beta.2 <1.3.0-0
               只允许同一补丁号下、预发布 >=beta.2 的版本,如 1.2.3-beta.4;1.2.4-beta.2 不行
  • ^version: 不允许改动最左边那个 非零 数字。因此 ^1.2.3 不跨主版本;^0.2.3 不跨次版本;^0.0.3 只允许补丁(再往下的预发布规则见下)
^1.2.3         等价于 >=1.2.3 <2.0.0-0
^0.2.3         等价于 >=0.2.3 <0.3.0-0
^0.0.3         等价于 >=0.0.3 <0.0.4-0
^1.2.3-beta.2  等价于 >=1.2.3-beta.2 <2.0.0-0
^0.0.3-beta    等价于 >=0.0.3-beta <0.0.4-0
^1.x           等价于 >=1.0.0 <2.0.0-0
^0.x           等价于 >=0.0.0 <1.0.0-0
  • 1.2.x: 1.2.0、1.2.1 等,不含 1.3.0
  • version1 - version2: 等价于 >=version1 <=version2
  • range1 || range2: 满足其一即可
  • tag: 发行标签,如 latest
  • * 或空字符串: 匹配任何版本
  • tarball URL、Git URL、GitHub 简写、本地路径

tarball URL

安装时下载该压缩包:

{
  "dependencies": {
    "asd": "http://asdf.com/asdf.tar.gz"
  }
}

Git URL

git+ssh://git@github.com:npm/cli.git#v1.0.27
git+ssh://git@github.com:npm/cli#semver:^5.0
git+https://isaacs@github.com/npm/cli.git
git://github.com/npm/cli.git#v1.0.27

GitHub URL

expressjs/express
mochajs/mocha#4727d357ea
user/repo#feature/branch

本地路径

../foo/bar
~/foo/bar
./foo/bar
/foo/bar

npm install -S ../foo/bar 会把路径规范成相对路径写进 package.json(npm 5 起 -S--save 已是默认,写不写效果相同):

{
  "dependencies": {
    "bar": "file:../foo/bar"
  }
}

适合本地联调尚未发布的包。

devDependencies(开发依赖)

开发期依赖,写法与 dependencies 相同,放测试工具、编译器、打包器等。

别人把你的包装进他们的项目时(npm install your-program),只会装你的 dependencies,不会装 devDependencies

若他们克隆了你的仓库,在仓库根目录执行 npm install,则会把 dependenciesdevDependencies 都装上,以便开发和测试。生产或 CI 若只要运行时依赖,用 npm install --omit=dev(旧旗标 --production 仍可用)。

peerDependencies(对等依赖)

表达与 宿主 的关系:插件 A1 必须在包 A 的环境下跑,则 A 是宿主,A1 把 A 写在 peerDependencies

{
  "name": "tea-latte",
  "version": "1.3.5",
  "peerDependencies": {
    "tea": "2.x"
  }
}

含义:我只在宿主 tea2.x 下工作;请保证安装我时,tea 也在同一层 node_modules,而不是嵌在 tea-latte 里面。

├── tea-latte@1.3.5
└── tea@2.2.0

当前树里已有 tea 但版本对不上 2.x 时,再 npm install tea-latte 会失败(npm 7+)或警告(npm 3-6)。

自动安装行为:

  • npm 1、2:自动装 peer。
  • npm 3-6:不自动装,只警告。
  • npm 7 起:默认再自动装。树里已有冲突版本则安装失败。临时可用 --legacy-peer-deps 回到 3-6 那种不强制装 peer 的行为。

插件的 peer 范围尽量宽,不要锁死到某一个补丁号,否则宿主稍一升级就会和别的插件冲突。

peerDependenciesMeta

为 peer 补充元数据。把某项标成 optional: true 后,用户没装这个 peer 时 npm 不再警告(该项仍应出现在 peerDependencies 里)。

{
  "name": "tea-latte",
  "version": "1.3.5",
  "peerDependencies": {
    "tea": "2.x",
    "soy-milk": "1.2"
  },
  "peerDependenciesMeta": {
    "soy-milk": {
      "optional": true
    }
  }
}

bundledDependencies(捆绑依赖)

官方字段名是 bundleDependenciesbundledDependencies 同样认。值为包名数组(版本以 dependencies 里的为准),也可为 true(捆全部依赖)或 false

本地要保留这些包装进 tarball、或只分发一个文件时,列出包名后执行 npm pack

{
  "name": "awesome-web-framework",
  "version": "1.0.0",
  "bundledDependencies": [
    "renderized",
    "super-streams"
  ]
}

npm pack 得到 awesome-web-framework-1.0.0.tgz,内含 renderizedsuper-streams。再用 npm install awesome-web-framework-1.0.0.tgz 装到新项目。

optionalDependencies(可选依赖)

这些依赖装不上或找不到时,npm 继续,不把整次安装判失败。使用方要自己做好缺失时的分支。

let foo = null;
let fooVersion;
try {
  foo = require('foo');
  fooVersion = require('foo/package.json').version;
} catch (er) {
  foo = null;
}
if (notGoodFooVersion(fooVersion)) {
  foo = null;
}
if (foo) {
  foo.doFooThings();
}

npm install --no-optional 会跳过它们。

optionalDependencies 会覆盖 dependencies 里的同名项,通常只在一处声明。

overrides

只在 项目根(workspace 根)的 package.json 生效,用来把依赖树里某个包换成指定版本或另一个包。嵌套可深可浅。

无论直接依赖声明了什么,都让 foo 装成 1.0.0

{
  "overrides": {
    "foo": "1.0.0"
  }
}

完整对象可以同时改包自身和它的子依赖。"." 表示 foo 自己:

{
  "overrides": {
    "foo": {
      ".": "1.0.0",
      "bar": "1.0.0"
    }
  }
}

只改子孙里的 foo

{
  "overrides": {
    "bar": {
      "foo": "1.0.0"
    }
  }
}

也可以按版本选择再嵌套:

{
  "overrides": {
    "baz@2.0.0": {
      "bar": {
        "foo": "1.0.0"
      }
    }
  }
}

直接依赖的范围和 override 对不上会报 EOVERRIDE。不要把 "foo": "^1.0.0" 的直接依赖覆盖成 ^2.0.0。范围一致则允许;更稳妥的是引用自身声明,不必再对一次版本:

{
  "dependencies": {
    "foo": "^1.0.0"
  },
  "overrides": {
    "foo": "$foo"
  }
}

overrides 改的是「已经声明的那条边解析到哪个版本」。若上游 package.json 漏写了依赖,要用根上的 packageExtensions(npm 较新版本)去补元数据,而不是用 overrides 硬塞一个不存在的边。该字段同样只认仓库根,非 private 的包若带上它,npm 会拒绝发布。

engines(引擎)

声明适用的 Node.js 或 npm 版本,默认 *(不限制):

{
  "engines": {
    "node": ">=18.0.0 <25",
    "npm": ">=10"
  }
}

除非用户设置了 engine-strictnpm config set engine-strict true),否则这只是建议:作为依赖安装时最多警告,不会阻止安装。

devEngines(较新的 npm)形状不同,用来约束 正在开发这份源码 的人用哪套 runtime、包管理器,在 installcirun 前检查;和面向「安装你这个包的用户」的 engines 不是一回事。

os

允许或禁止的操作系统。值来自 process.platform! 表示排除。

{
  "os": [
    "darwin",
    "linux",
    "!win32"
  ]
}

cpu

允许或禁止的 CPU 架构。值来自 process.arch

{
  "cpu": [
    "x64",
    "ia32",
    "!arm",
    "!mips"
  ]
}

Linux 上若还要限制 C 库,可用 libc(如 "glibc""musl"),且通常配合 "os": "linux"

private

"private": true 时,npm publish 会拒绝。用来防止把私有项目发到公共 registry。

若允许发布、但只准发到指定仓库(如内网),用下面的 publishConfig.registry

publishConfig

发布时使用的配置,常见 tagregistryaccess。范围包默认 restricted,要发到公共源需 "access": "public"

可覆盖的项见 npm 的 config 文档。

workspaces

工作区路径或 glob。每个工作区目录里要有有效的 package.json,安装时会链到顶层 node_modules

{
  "name": "workspace-example",
  "workspaces": [
    "./packages/*"
  ]
}

packageManager

Corepack 看的包管理器及精确版本,例如 "packageManager": "npm@10.9.2"。团队用来钉死 npm、pnpm、Yarn 的版本,避免各人 CLI 不一致。

默认值

npm 会根据包内容补一些默认:

  • "scripts": { "start": "node server.js" }

    根目录有 server.js 且未定义 start 时。

  • "scripts": { "install": "node-gyp rebuild" }

    根目录有 binding.gyp,且没有定义 installpreinstall 时。

  • "contributors": [...]

    根目录有 AUTHORS 时,每行按 Name <email> (url) 解析,email 和 url 可选。以 # 开头或空行忽略。

常见问题

npm ci 和 npm install 的区别

npm ci 面向自动化:测试、CI、部署,或你要一次不掺杂质的全新安装。有 lockfile、且 node_modules 为空或不存在时,通常比反复决策版本的 npm install 更快。

主要区别:

  • 必须已有 package-lock.jsonnpm-shrinkwrap.json,否则 npm ci 不能用;
  • lockfile 与 package.json 对不上就 退出报错,不会去改 lockfile;
  • 一次只装整个项目,不能顺便 npm ci lodash 这种加单个包;
  • 开始前会删除 node_modules
  • 不改 package.json 和 lockfile。

持续集成和生产发布优先 npm ci:按 lockfile 里的精确版本和 integrity 安装,避免「我这机能装、流水线对不上」。

dependencies 和 devDependencies 的区别

dependencies: 运行或发布后仍需要的包,例如框架 Vue、UI 组件库。npm install <packageName>(可写 -S--save,npm 5 起已是默认)写进这里。不写版本则按 registry 当时符合默认保存规则的版本写入(通常是带 ^ 的当前最新)。指定版本写成 一个 参数:npm install vue@3.0.1。写成 vue @3.0.1 会被当成两个包名。

devDependencies: 只在开发期需要,例如 webpack、babel、测试框架。npm install <packageName> -D

业务应用里执行 npm install 时两类都会下载。会不会进最终产物,取决于打包器有没有分析到你的源码 importrequire 了它,不是看它写在哪一个字段。

发布 npm 包时:别人安装你的包,只会带上你的 dependencies(以及 peer 等),不会带上你的 devDependencies

package.json 和 package-lock.json 的区别

package.json 写的是 允许的范围(如 ^1.2.3)。package-lock.json 写的是某一次解析得到的 整棵精确树(版本、完整性哈希、下载地址)。删除 node_modulesnpm install 时,有 lockfile 就能复现同一棵树,也少打一些「再选一遍最新补丁」的决策。lockfile 应提交进版本库;发布到 registry 的 tarball 默认 不含 package-lock.json

参考资料

package.json(npm 文档)

scripts 生命周期

Node.js 包入口(exports、imports)

Node.js 中文网 - package.json 指南

© lizhao all right reserved,powered by Gitbook文件修订时间: 2026-09-02 00:47:03

results matching ""

    No results matching ""