Skip to content

包管理工具详解 ​

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 包管理器。

优点

  1. 生态系统最大,几乎所有的 JavaScript 包都可以在 npm 上找到。
  2. 默认随 Node.js 安装,无需额外设置。
  3. npm scripts 功能强大,可以方便地定义和运行项目脚本。

缺点

  1. 早期版本安装速度较慢,特别是在处理大型项目时。
  2. node_modules 结构可能很深,导致文件路径过长(尤其在 Windows 上)。
  3. 包安装的确定性较低(npm5 之前)。
  4. 使用 package-lock.json 文件来锁定依赖版本,提高安装的确定性,但还是没有解决依赖冗余的问题
  5. 幽灵依赖:多个版本的子依赖,第一个会被安装到 node_modules,package.json没有声明却会被访问到

yarn ​

Yarn 由 Facebook 开发,旨在解决 npm 的一些问题。

优点

  1. 安装速度快,利用并行下载和缓存机制。
  2. 提供了更好的依赖确定性,通过 yarn.lock 文件精确锁定版本。
  3. 离线模式支持,可以从缓存安装包。
  4. 更友好的 CLI 输出。

缺点:

  1. 需要单独安装,不随 Node.js 默认提供。
  2. 某些情况下可能与 npm 产生兼容性问题。

pnpm ​

pnpm 是较新的包管理器,专注于提高效率和节省磁盘空间。 创建了一个内存地址,然后在.pnpm中存放了非扁平的虚拟地址,node_modules中的文件再继续指向.pnpm中的文件。

优点:

  1. 非常快速,pnpm通过hard link(硬连接)机制,节省磁盘空间并提升安装速度。
  2. 建立非扁平的node_modules目录,依赖包都是sybolic link(软链接),目录里的package都软链到.pnpm下这样的路径。
  3. node_modules目录是非扁平的,不存在由于依赖提升带来的幽灵依赖问题。
  4. 支持 monorepo 结构。

缺点: 相对较新,生态系统和社区支持可能不如 npm 和 Yarn 广泛。 某些旧项目或特定工具可能与其 node_modules 结构不兼容。