yarn和npm

208 阅读6分钟

包管理器 (Package Manager) 是什么

yarn和npm都是包管理器, 包管理器是一种用于创建项目环境并轻松导入外部依赖项的工具;

通过使用包管理器, 可以自动化安装(install), 升级(upgrade), 配置(configure)和从项目环境中删除(remove)依赖项的过程

yarn 和 npm是什么

npm代表node包管理器,它是js软件包的库和注册表,npm也有命令行工具来帮助你安装不同的包并管理它们的依赖关系.

yarn [Yet Another Resource Navigator]

yarn也是一个js包和依赖项管理器, 它的出现是为了解决旧版本npm cli的缺点(例如: 版本锁定), 因为yarn是建立在npm注册表之上的, 所以在npm发布的包也可以在yarn上使用, 这有助于无缝升级. 其次yarn提供速度、一致性、稳定性和安全性作为npm的替代品次

similar 相似处

  1. 它们都会自动生成一个版本锁定文件, 用于跟踪项目使用的确切依赖项列表
  2. 它们都提供了在离线缓存中保存依赖项的选项, 允许即使在离线时也可以安装依赖项
  3. 它们都支持工作区, 允许在单个respository管理大量project的依赖

区别

1. 锁文件

  • yarn生成yarn.lock

  • npm生成package-lock.json

    由npm创建的package-lock.json文件也被yarn支持, 可以轻松地将版本数据从npm迁移到yarn

2. speed(包安装过程)

yarn并行(parallel)安装包, yarn被优化为一次获取和安装多个包.

npm串行(sequentially)安装包,它独立安装每一个包.

所以yarn的安装过程比npm更快

3. yarn独特特性

  1. Plug‘n’Play

从yarn 2版本开始, 它不再使用node_modules文件夹; 它会生成一个映射项目依赖项的.pnp.cjs文件. 这会产生更优化的依赖树和更快的项目启动和包安装

  1. Zero-Installs

此功能与Plug‘n’Play结合使用,后者使用.pnp.cjs文件映射离线缓存中的包; 这使可以快速检索和安装已保存(saved)的包

yarn.lock 如何阅读它

simple dependency(简单依赖)

"@xtuc/long@4.2.2":
  version "4.2.2"
  resolved "https://registry.npmmirror.com/@xtuc/long/-/long-4.2.2.tgz"
  integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==

arg@^4.1.0:
  version "4.1.3"
  resolved "https://registry.npmmirror.com/arg/-/arg-4.1.3.tgz"
  integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==

arg是依赖项, 在请求大于4.1.0版本(arg@^4.1.0)被请求,

version: 4.1.3 表示解析的版本

reolved: 下载的url及hash

integrity: 完整的哈希

multiple resolutions

abab@^2.0.3, abab@^2.0.5:
  version "2.0.6"
  resolved "https://registry.npmmirror.com/abab/-/abab-2.0.6.tgz"
  integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==

可以看到abab以2个版本导入: 2.0.3, 2.0.5

但在解析时, 两者都解析为相同的版本: 2.0.6 因此最终两者都将使用具有相同版本的包(package)

Dependency with dependencies (依赖的依赖)

bonjour@^3.5.0:
  version "3.5.0"
  resolved "https://registry.npmmirror.com/bonjour/-/bonjour-3.5.0.tgz"
  integrity sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==
  dependencies:
    array-flatten "^2.1.0"
    deep-equal "^1.0.1"
    dns-equal "^1.0.0"
    dns-txt "^2.0.2"
    multicast-dns "^6.0.1"
    multicast-dns-service-types "^1.1.0"
    
array-flatten@^2.1.0:
  version "2.1.2"
  resolved "https://registry.npmmirror.com/array-flatten/-/array-flatten-2.1.2.tgz"
  integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==

这里的bonjour是用3.5.0版本导入的, 也是用3.5.0版本解析的, 这个版本的bonjour需要依赖其他模块(array-flatten, deep-equal, dns-equal, dns-txt, multicast-dns, multicast-dns-service-types)

array-flatten "^2.1.0" 其中2.1.0是请求导入的版本, 但不一定是解析的版本, 具体解析的版本要找到对应的array-flatten, 以上解析的版本是2.1.2

更复杂的例子


array-flatten@1.1.1:
  version "1.1.1"
  resolved "https://registry.npmmirror.com/array-flatten/-/array-flatten-1.1.1.tgz"
  integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==

array-flatten@^2.1.0:
  version "2.1.2"
  resolved "https://registry.npmmirror.com/array-flatten/-/array-flatten-2.1.2.tgz"
  integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==
  
express@^4.16.3, express@^4.17.1:
  version "4.18.1"
  resolved "https://registry.npmmirror.com/express/-/express-4.18.1.tgz"
  integrity sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==
  dependencies:
    accepts "~1.3.8"
    array-flatten "1.1.1"
    body-parser "1.20.0"
    content-disposition "0.5.4"
    content-type "~1.0.4"
    cookie "0.5.0"
    cookie-signature "1.0.6"
    debug "2.6.9"
    depd "2.0.0"
    encodeurl "~1.0.2"
    escape-html "~1.0.3"
    etag "~1.8.1"
    finalhandler "1.2.0"
    fresh "0.5.2"
    http-errors "2.0.0"
    merge-descriptors "1.0.1"
    methods "~1.1.2"
    on-finished "2.4.1"
    parseurl "~1.3.3"
    path-to-regexp "0.1.7"
    proxy-addr "~2.0.7"
    qs "6.10.3"
    range-parser "~1.2.1"
    safe-buffer "5.2.1"
    send "0.18.0"
    serve-static "1.15.0"
    setprototypeof "1.2.0"
    statuses "2.0.1"
    type-is "~1.6.18"
    utils-merge "1.0.1"
    vary "~1.1.2"

这里可以看到1.1.1和^2.1.0都请求了array-flatten, 但是2.1.0解析的版本是2.1.2, 1.1.1的版本解析的是1.1.1

/node_modules
    /array-flatten@2.1.2
    /express
        /node_modules
            /array-flatten@1.1.1

因为两个不同的版本, 所以在node_module存在一个2.1.2的array-flatten包, 以及node_modules/express/node_modules也有一个1.1.1的array-flatten包

yarn.lock文件中重复文件(缩短yarn install时间)


"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.17.2", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.6", "@babel/runtime@^7.7.7", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2", "@babel/runtime@^7.9.6":
  version "7.18.6"
  resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.18.6.tgz"
  integrity sha512-t9wi7/AW6XtKahAe20Yw0/mMljKq0B1r2fPdvaAdV/KPDZewFXdaaa6K7lxmZBZ8FBNpCiAT6iHPmd6QO9bKfQ==
  dependencies:
    regenerator-runtime "^0.13.4"

"@babel/runtime@^7.16.7":
  version "7.21.0"
  resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673"
  integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==
  dependencies:
    regenerator-runtime "^0.13.11"

运行yarn-deduplicate yarn.lock

"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.16.7", "@babel/runtime@^7.17.2", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.6", "@babel/runtime@^7.7.7", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2", "@babel/runtime@^7.9.6":
  version "7.21.0"
  resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673"
  integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==
  dependencies:
    regenerator-runtime "^0.13.11"

对比以上能发现, @babel/runtime不同的版本被整合到了一起, 通过yarn-deduplicate等工具自动化帮助你删除依赖项中的重复项, 这有助于缩短yarn install安装依赖项的时间(这里只能整合次要版本和补丁版本不同的两个包, 比如上面的7.18.6 和 7.21.0, 而对于array-flatten版本2和版本1无法整合)

.yarn-integrity文件

在项目根node_modules文件夹中有个秘密文件.yarn-integrity文件,yarn在文件中写下了它需要知道的关于仓库及其安装依赖项的所有信息.

如果此文件的内容与仓库中的包不匹配, yarn将更新它并刷新已安装的包;

如果文件丢失,它将基于yarn.lock生成它,并更新node_modules;

dev.to/ayc0/yarn-l…

package-lock.json

首先, 在项目根目录下, 通过npm install就会生成package-lock.json的文件

什么时候npm会重写package-lock.json文件?

例子:

声明了一个package.json文件

"foo": "^2.3.0"

然后, 通过 npm install 将生成一个packge-lock.json

"foo": "2.3.0"

几天后, 发布了一个更新的foo次要版本“2.4.0”, 然后执行以下两个命令:

  1. npm install - package-lock.json锁定的版本在范围内(^2.3.0) 所以安装了2.3.0
  2. npm ci - 它无论如何只查看package-lock.json, 所以安装了2.3.0

接下来, 你手动将package.json更新为

"foo": "^2.4.0"

依然执行以下两个命令:

  1. npm install - package-lock.json锁定的版本(2.3.0)不在范围内(^2.4.0), 所以会重写为2.4.0
"foo": "2.4.0"
  1. npm ci - 无论如何只查看package-lock.json文件,但由于版本不在范围内, 它会抛出错误

npm ci和npm install类似, 不同之处在于npm ci用于自动化环境, 例如测试平台、持续集成和部署 - 或者任何你想要确保干净安装依赖项的情况

package-lock.json的作用有哪些?

  1. Dependency version tracking(依赖版本跟踪): 它(package-lock.json)跟踪项目中当前安装的依赖项和子依赖项的确切版本
  2. Consistent builds(一致的构建): 由于它记录了确切版本的依赖项, 它可以确保在项目上工作的每个人都安装了相同的依赖项,这有助于避免版本冲突并确保项目可以在不同环境中一致地构建和运行
  3. Faster and more reliable installs(更快、更可靠的安装): 它允许npm快速准确地在不同机器上安装相同版本的依赖项,这使得安装过程更快、更可靠
  4. Security(安全性): 它通过确保只安装经过验证和安全的依赖项版本来帮助防止恶意代码注入
  5. Reproducible builds(可重现的构建): 通过文件中记录依赖项特定的版本, 可以更轻松地在以后或不同的机器上重现构建,因为可以简单地使用相同的package-lock.json文件来安装相同的依赖想版本