包管理工具详解
package.json

必备属性
package.json 中有非常多的属性,其中必须填写的只有两个:name 和 version ,这两个属性组成一个 npm 模块的唯一标识。
name:name即模块名称,需要遵循官方的一些规范和建议
- 可以使用validate-npm-package-name来检测包名是否合法
- 可以使用
npm view packageName命令来查看一个包是否已经存在,并可以获取包相关信息
描述信息
基本描述:
description:用于添加模块的的描述信息,方便别人了解你的模块。keywords:用于给你的模块添加关键字。 当然,他们的还有一个非常重要的作用,就是利于模块检索。当你使用npm search检索模块时,会到description和keywords中进行匹配。写好description和keywords有利于你的模块获得更多更精准的曝光
开发人员
author:指包的主要作者,一个author对应一个人。contributors: 指贡献者信息,一个contributors对应多个贡献者,值为数组,
地址
homepage: 用于指定该模块的主页;repository: 用于指定模块的代码仓库;bugs: 指定一个地址或者一个邮箱,对你的模块存在疑问的人可以到这里提出问题。
目录&文件相关
main: 指定程序的主入口文件,例如,antd指定的模块入口lib/index.js,当我们在代码用引入antd时:import { notification } from 'antd'; 实际上引入的就是lib/index.js中暴露出去的模块。bin: 指定模块的命令行入口,如:bin:{ "my-module": "./lib/cli.js" }files: 用于描述你npm publish后推送到npm服务器的文件列表,如果指定文件夹,则文件夹内的所有内容都会包含进来。,默认为["lib"],即打包lib目录下的文件, 另外,你还可以通过配置一个.npmignore文件来排除一些文件, 防止大量的垃圾文件推送到npm, 规则上和你用的.gitignore是一样的。.gitignore文件也可以充当.npmignore文件。man:man命令是 Linux 下的帮助指令,通过man指令可以查看 Linux 中的指令帮助、配置文件帮助和编程帮助等信息。,如:man:["doc/man1/foo.1"]directories: 用于描述模块的目录,例如一个规范项目目录:
json{ "directories": { "lib": "src/lib/", //存放js代码的目录; "bin": "src/bin/", //存放可执行二进制文件的目录; "man": "src/man/", //存放 man 文档的目录; "doc": "src/doc/", //存放文档的目录; "test": "src/test/" //存放单元测试用例代码的目录; } }依赖配置
dependencies: 用于描述模块的依赖,如:dependencies:{ "react": "^0.14.0", "react-dom": "^0.14.0" }
json{ "dependencies": { "foo": "1.0.0 - 2.9999.9999", "bar": ">=1.0.2 <2.1.2", "baz": ">1.0.2 <=2.3.4", "boo": "2.0.1", "qux": "<1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 <3.0.0", "asd": "http://asdf.com/asdf.tar.gz", // 从远程下载 "til": "~1.2", // 1.2.x "elf": "~1.2.3", "two": "2.x", "thr": "3.3.x", "lat": "latest", "dyl": "file:../dyl", // 本地文件 "kpg": "npm:pkg@1.0.0" // 包的自定义别名 } }devDependencies: 用于描述开发阶段的依赖,如:devDependencies:{ "babel-core": "^6.0.0", "babel-preset-es2015": "^6.0.0" }peerDependencies: 用于描述运行时依赖,如:peerDependencies:{ "react": "^0.14.0" }
脚本配置
scripts: 用于配置一些脚本命令的缩写pre和post前缀,用于描述在命令执行前和命令执行后要执行的脚本,如:scripts:{ "prepublish": "npm run build", "postpublish": "npm run deploy" }config: 字段用于配置脚本中使用的环境变量,例如下面的配置,可以在脚本中使用process.env.npm_package_config_port进行获取。
json{ "config" : { "port" : "8080" } }发布配置
engines: 指定模块的运行环境,如:engines:{ "node": ">=0.10.3 <0.12" }private: 指定模块是否为私有模块,私有模块不会被npm发布到npm服务器,如:private:true
包管理工具
npm install

嵌套结构
npm 的早期版本, npm 处理依赖的方式简单粗暴,以递归的形式,严格按照 package.json 结构以及子依赖包的 package.json 结构将依赖安装到他们各自的 node_modules 中。直到有子依赖包不在依赖其他模块。
优点:
node_modules的结构和package.json结构一一对应,层级结构明显,并且保证了每次安装目录结构都是相同的。 缺点:- 如果你依赖的模块非常之多,你的
node_modules将非常庞大,嵌套层级非常之深 - 在不同层级的依赖中,可能引用了同一个模块,导致大量冗余;
- 在 Windows 系统中,文件路径最大长度为260个字符,嵌套层级过深可能导致不可预知的问题;
扁平结构
为了解决以上问题,NPM 在 3.x 版本做了一次较大更新。其将早期的嵌套结构改为扁平结构:
- 安装模块时,不管其是直接依赖还是子依赖的依赖,优先将其安装在 node_modules 根目录。
- 当安装到相同模块时,判断已安装的模块版本是否符合新模块的版本范围,如果符合则跳过,不符合则在当前模块的
node_modules下安装该模块。
缺点:
- 并未完全解决模块冗余的问题
- 带来了新的问题,假如你的A依赖了C1,B依赖了C2,如果你的
package.json的顺序是A,B,那么会先安装A,B,C1,再把C2安装到B的node_modules下,但是如果顺序是B,A,那么会先安装B,C2,再把C1安装到A的node_modules下,这样依赖结构的不确定性可能会给程序带来不可预知的问题。
Lock文件
为了解决 npm install 的不确定性问题,在 npm 5.x 版本新增了 package-lock.json 文件,而安装方式还沿用了 npm 3.x 的扁平化的方式。 package-lock.json 的作用是锁定依赖结构,即只要你目录下有 package-lock.json 文件,那么你每次执行 npm install 后生成的 node_modules 目录结构一定是完全相同的。 同时,package-lock.json 中已经缓存了每个包的具体版本和下载链接,不需要再去远程仓库进行查询,然后直接进入文件完整性校验环节,减少了大量网络请求。
npm(Node Package Manager)
npm 是最早且最广泛使用的 Node.js 包管理器。
优点
- 生态系统最大,几乎所有的 JavaScript 包都可以在 npm 上找到。
- 默认随 Node.js 安装,无需额外设置。
- npm scripts 功能强大,可以方便地定义和运行项目脚本。
缺点
- 早期版本安装速度较慢,特别是在处理大型项目时。
- node_modules 结构可能很深,导致文件路径过长(尤其在 Windows 上)。
- 包安装的确定性较低(npm5 之前)。
- 使用 package-lock.json 文件来锁定依赖版本,提高安装的确定性,但还是没有解决依赖冗余的问题
- 幽灵依赖:多个版本的子依赖,第一个会被安装到 node_modules,package.json没有声明却会被访问到
yarn
Yarn 由 Facebook 开发,旨在解决 npm 的一些问题。
优点
- 安装速度快,利用并行下载和缓存机制。
- 提供了更好的依赖确定性,通过 yarn.lock 文件精确锁定版本。
- 离线模式支持,可以从缓存安装包。
- 更友好的 CLI 输出。
缺点:
- 需要单独安装,不随 Node.js 默认提供。
- 某些情况下可能与 npm 产生兼容性问题。
pnpm
pnpm 是较新的包管理器,专注于提高效率和节省磁盘空间。 创建了一个内存地址,然后在.pnpm中存放了非扁平的虚拟地址,node_modules中的文件再继续指向.pnpm中的文件。
优点:
- 非常快速,pnpm通过
hard link(硬连接)机制,节省磁盘空间并提升安装速度。 - 建立非扁平的
node_modules目录,依赖包都是sybolic link(软链接),目录里的package都软链到.pnpm下这样的路径。 node_modules目录是非扁平的,不存在由于依赖提升带来的幽灵依赖问题。- 支持 monorepo 结构。
缺点: 相对较新,生态系统和社区支持可能不如 npm 和 Yarn 广泛。 某些旧项目或特定工具可能与其 node_modules 结构不兼容。