Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【译文】Yarn 的确定性 #3

Open
fengliner opened this issue Jan 1, 2018 · 4 comments
Open

【译文】Yarn 的确定性 #3

fengliner opened this issue Jan 1, 2018 · 4 comments

Comments

@fengliner
Copy link
Owner

原文https://yarnpkg.com/blog/2017/05/31/determinism/

Yarn 提出的其中一个观点是它让包管理变成确定性的。但这到底意味着什么呢?这篇博文突出说明了 yarn
和 npm 5是如何保证包管理是确定性的,并且指出它们提供的确切保证和做出选择的权衡点的不同。

什么是确定性

在 Javascript 包管理的背景下,确定性是指在给定的 package.json 和 lock 文件下始终能得到一致的 node_modules 目录结构。

Yarn 确定性的保证因素

Lockfile

只要使用相同的 yarn 版本,yarn 就是完全确定性的。在 yarn 和 npm 5 中,确定性是由 lockfile 文件确保的,lockfile 包含了整个依赖树的信息。然而,在 yarn 和 npm 5中,lockfile 的文件格式是不同的。我们从来没有公开谈论过为什么使用这种格式,这里我们会带你一起看一下。

如果你曾经看过 yarn.lock 文件,你应该对下面的这种结构感到很熟悉。

has-flag@^1.0.0:
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"

supports-color@^3.2.3:
  version "3.2.3"
  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
  dependencies:
    has-flag "^1.0.0"

这是通过运行命令 yarn add supports-color而产生的 yarn.lock 文件。这个文件包含了 supports-color 和 它的子依赖 has-flag 的确切版本。

使用这个 lockfile 文件,我们可以确定 supports-color 依赖的 has-flag 总是使用相同的版本。

但是 lockfile 文件缺少一个关键的信息,那就是依赖树上的每一个依赖的 提升(hoisting) 和 位置。比如,给定一个 yarn.lock 文件是不可能确定顶层依赖的,除非同时拥有 package.json 文件。即使知道了顶层依赖,我们仍然无法推断出应该依赖装在什么位置。

实际上,这意味着在 node_modules 目录中 package 的位置是在 yarn 的内部计算出来的。这就使得在使用不同版本的 yarn 时可能会引起不确定性。

我们这样做的原因是 这种 lockfile 文件的格式对 比对(diffing) 是非常有用的。也就是说,对 lockfile 的修改很容易被人审查。我们使用自定义格式而不是JSON,并将所有内容都放在顶层,这样便于阅读和审查。合并冲突通常由版本控制自动处理,这样就可以减少冲突。

提升保证

尽管在不同的版本中,yarn 提升可能会有所不同。但在使用相同版本的 yarn 时,我们仍然会对提升进行强有力的保证。这些保证中最重要的是忽略诸如 optionalDependencies 和 devDependencies 这样的会影响正常依赖关系的位置的环境依赖。

这实在有些让人难以理解,那到底是什么意思呢?

在 package.json 文件里你可以声明多种类型的依赖。其中两个,一个是始终会安装的简单依赖,另一个是只有在 package.json 目录下运行 npm installyarn 才会安装的 dev 依赖。

由于这些特性,在忽略了某些依赖后可能会导致不同的 node_modules 目录结构。但是在确定依赖在 node_modules 中的确定位置时,yarn 仍然会把所有的依赖考虑在内。即使有些依赖没有被安装,他们仍然会影响其他依赖的提升位置。这一点很重要,因为在生产和开发环境中,包的提升位置有差异会导致非常奇怪的 bug。

注:这个保证不是 yarn 唯一的,npm 5 也是这样做的。

和 npm 5 比较

npm 5 引入了一个称为 package-lock 的类似 shrinkwrap 特性的 设计。package-lock 文件包含了创建node_modules 所需的所有信息以及所有的提升信息。先前的 yarn.lock 文件的 npm 版本是如下的结构:

{
  "name": "react-example",
  "version": "1.0.0",
  "lockfileVersion": 1,
  "dependencies": {
    "has-flag": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
      "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo="
    },
    "supports-color": {
      "version": "3.2.3",
      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
      "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY="
    }
  }
}

注意,在第一个 dependencies 对象里列出的所有依赖都是提升的。这就意味着 npm 仅仅使用 package-lock 文件就可以确定最终的依赖关系图,而 yarn 却需要相应的 package.json 文件。

这意味着 npm 可以更好地保证在 不同的 npm 版本中提升位置,而代价是拥有一个更密集的锁文件。对于 yarn 来说,关于 lockfile 的互操作性还不是很清楚,所以目前还没有关于如何支持 package-lock 的计划。但是,你可以想象在将来 yarn 会同时支持和更新他们。我们对社区的反馈非常感兴趣,并鼓励人们就如何将其作为RFC提出建议。

小结

每个 lock 文件格式都有不同的考虑方案,而且似乎没有完美的格式。在决定使用什么包管理器时,评估你需要什么样的保证是很重要的。

npm 5 具有更强的跨版本的保证,并且具有更强的确定性锁文件。但是只有当你使用同一个版本时,yarn 才有这些保证,因为这样会更有利于一个更轻量的锁文件以及更好的审查。很可能会有一个更好的 lockfile 的解决方案,但是 yarn 和 npm 5 的锁文件代表了当前的状态,并且可能在未来发生融合。

希望这篇文章让您了解 yarn 和 npm 之间的确定性保证的不同,并帮助您决定什么对您的公司或项目更有效。

@fengliner fengliner changed the title [译文] Yarn 的确定性 【译文】Yarn 的确定性 Jan 1, 2018
@fengliner
Copy link
Owner Author

原文中的 hoisting 一词,这里翻译为了“提升”,但总感觉不准确,有了解的还请赐教

@jasonslyvia
Copy link

这篇文章里提到的 hoisting 和 JavaScript 中的 hoisting 应该是一个意思,译成「提升」没毛病,参考资料 MDN

@cunjieliu
Copy link

👍

@MuYunyun
Copy link

MuYunyun commented Oct 18, 2018

我觉得通过 yarn.lock 文件也是能推断出 node_modules 顶层目录的。比如下方

// yarn 的 yarn.lock 类似如下
- a
- b
- c
  - xxx@1.0.1
- xxx@1.0.1
- xxx@1.0.0

这个例子中,我们能得出顶层目录是

// node_modules
- a
- b
- c
- xxx@1.0.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants