工作空间

工作区是一种设置包架构的新方法,从 Yarn 1.0 开始默认可用。它允许你以这样的方式设置多个软件包,只需运行 yarn install 一次即可一次性安装所有软件包。

¥Workspaces are a new way to set up your package architecture that’s available by default starting from Yarn 1.0. It allows you to setup multiple packages in such a way that you only need to run yarn install once to install all of them in a single pass.

你为什么想做这个?

¥Why would you want to do this?

  • 你的依赖可以链接在一起,这意味着你的工作区可以相互依赖,同时始终使用最新的可用代码。这也是比 yarn link 更好的机制,因为它只影响你的工作区树而不是整个系统。

    ¥Your dependencies can be linked together, which means that your workspaces can depend on one another while always using the most up-to-date code available. This is also a better mechanism than yarn link since it only affects your workspace tree rather than your whole system.

  • 所有项目依赖都将安装在一起,为 Yarn 提供更多自由度来更好地优化它们。

    ¥All your project dependencies will be installed together, giving Yarn more latitude to better optimize them.

  • Yarn 将使用单个锁定文件,而不是为每个项目使用不同的锁定文件,这意味着更少的冲突和更容易的审查。

    ¥Yarn will use a single lockfile rather than a different one for each project, which means fewer conflicts and easier reviews.

如何使用它?

¥How to use it?

package.json 文件中添加以下内容。从现在开始,我们将该目录称为 “工作区根目录”:

¥Add the following in a package.json file. Starting from now on, we’ll call this directory the “workspace root”:

package.json

{
  "private": true,
  "workspaces": ["workspace-a", "workspace-b"]
}

请注意,private: true 是必需的!工作区不应该被发布,因此我们添加了此安全措施,以确保没有任何东西可以意外暴露它们。

¥Note that the private: true is required! Workspaces are not meant to be published, so we’ve added this safety measure to make sure that nothing can accidentally expose them.

创建此文件后,创建两个名为 workspace-aworkspace-b 的新子文件夹。在每个文件中,创建另一个包含以下内容的 package.json 文件:

¥After this file has been created, create two new subfolders named workspace-a and workspace-b. In each of them, create another package.json file with the following content:

workspace-a/package.json:

{
  "name": "workspace-a",
  "version": "1.0.0",

  "dependencies": {
    "cross-env": "5.0.5"
  }
}

workspace-b/package.json:

{
  "name": "workspace-b",
  "version": "1.0.0",

  "dependencies": {
    "cross-env": "5.0.5",
    "workspace-a": "1.0.0"
  }
}

最后,在某个地方运行 yarn install,最好是在工作区根目录内。如果一切正常,你现在应该有一个类似的文件层次结构:

¥Finally, run yarn install somewhere, ideally inside the workspace root. If everything works well, you should now have a similar file hierarchy:

/package.json
/yarn.lock

/node_modules
/node_modules/cross-env
/node_modules/workspace-a -> /workspace-a

/workspace-a/package.json
/workspace-b/package.json

注意:不要寻找 /node_modules/workspace-b。除非其他包将其用作依赖,否则它不会存在。

¥Note: don’t look for /node_modules/workspace-b. It won’t be there unless some other package use it as a dependency.

就是这样!从位于 workspace-b 的文件中请求 workspace-a 现在将使用当前位于项目内部的确切代码,而不是在 npm 上发布的代码,并且 cross-env 包已被正确删除重复数据并放置在项目的根目录中,以供 workspace-aworkspace-b

¥And that’s it! Requiring workspace-a from a file located in workspace-b will now use the exact code currently located inside your project rather than what is published on npm, and the cross-env package has been correctly deduped and put at the root of your project to be used by both workspace-a and workspace-b.

请注意,/workspace-a 通过符号链接别名为 /node_modules/workspace-a。这就是允许你像普通包一样需要该包的技巧!你还需要知道使用的是 /workspace-a/package.json#name 字段,而不是文件夹名称。这意味着如果 /workspace-a/package.json name 字段是 "pkg-a",则别名将如下:/node_modules/pkg-a -> /workspace-a,你将能够使用 const pkgA = require("pkg-a");(或者可能是 import pkgA from "pkg-a";)从 /workspace-a 导入代码。

¥Please note the fact that /workspace-a is aliased as /node_modules/workspace-a via a symlink. That’s the trick that allows you to require the package as if it was a normal one! You also need to know that the /workspace-a/package.json#name field is used and not the folder name. This means that if the /workspace-a/package.json name field was "pkg-a", the alias will be the following: /node_modules/pkg-a -> /workspace-a and you will be able to import code from /workspace-a with const pkgA = require("pkg-a"); (or maybe import pkgA from "pkg-a";).

与 Lerna 相比如何?

¥How does it compare to Lerna?

Yarn 的工作区是 Lerna(以及 do!)等工具可以使用的底层基础类型。他们永远不会尝试支持 Lerna 提供的高级功能,但通过在 Yarn 本身内部实现解析的核心逻辑和链接步骤,我们希望能够实现新的用法并提高性能。

¥Yarn’s workspaces are the low-level primitives that tools like Lerna can (and do!) use. They will never try to support the high-level feature that Lerna offers, but by implementing the core logic of the resolution and linking steps inside Yarn itself we hope to enable new usages and improve performance.

提示与技巧

¥Tips & Tricks

  • workspaces 字段是一个数组,其中包含每个工作区的路径。由于跟踪它们中的每一个可能很乏味,因此该字段还接受通配符模式!例如,Babel 通过单个 packages/* 指令引用其所有包。

    ¥The workspaces field is an array containing the paths to each workspace. Since it might be tedious to keep track of each of them, this field also accepts glob patterns! For example, Babel reference all of their packages through a single packages/* directive.

  • 工作区足够稳定,可以在大型应用中使用,并且不应改变常规安装的工作方式,但如果你认为它们破坏了某些内容,可以通过将以下行添加到 Yarnrc 文件中来禁用它们:

    ¥Workspaces are stable enough to be used in large-scale applications and shouldn’t change anything from the way the regular installs work, but if you think they’re breaking something, you can disable them by adding the following line into your Yarnrc file:

    workspaces-experimental false
    
  • 如果你仅对单个工作区进行更改,请使用 –focus 从注册表快速安装同级依赖,而不是从头开始构建所有依赖。

    ¥If you’re only making changes to a single workspace, use –focus to quickly install sibling dependencies from the registry rather than building all of them from scratch.

限制和注意事项

¥Limitations & Caveats

  • 你的工作区和用户将获得的包布局将有所不同(工作区依赖将提升到文件系统层次结构中的更高位置)。对这种布局做出假设已经很危险了,因为提升过程没有标准化,所以理论上这里没有什么新鲜事。如果遇到问题,请尝试使用 nohoist 选项

    ¥The package layout will be different between your workspace and what your users will get (the workspace dependencies will be hoisted higher into the filesystem hierarchy). Making assumptions about this layout was already hazardous since the hoisting process is not standardized, so theoretically nothing new here. If you encounter issues, try using the nohoist option

  • 在上面的示例中,如果 workspace-b 依赖的版本与 workspace-a 的 package.json 中引用的版本不同,则将从 npm 安装依赖,而不是从本地文件系统链接。这是因为有些包实际上需要使用以前的版本才能构建新版本(Babel 就是其中之一)。

    ¥In the example above, if workspace-b depends on a different version than the one referenced in workspace-a’s package.json, the dependency will be installed from npm rather than linked from your local filesystem. This is because some packages actually need to use the previous versions in order to build the new ones (Babel is one of them).

  • 在工作区中发布包时要小心。如果你正在准备下一个版本,并且决定使用新的依赖,但忘记在 package.json 文件中声明它,则如果另一个包已将该依赖下载到工作区根目录中,你的测试可能仍会在本地通过。但是,对于从注册表中提取它的消费者来说,它将被破坏,因为依赖列表现在不完整,因此他们无法下载新的依赖。目前无法在这种情况下触发警告。

    ¥Be careful when publishing packages in a workspace. If you are preparing your next release and you decided to use a new dependency but forgot to declare it in the package.json file, your tests might still pass locally if another package already downloaded that dependency into the workspace root. However, it will be broken for consumers that pull it from a registry, since the dependency list is now incomplete so they have no way to download the new dependency. Currently there is no way to throw a warning in this scenario.

  • 就文件夹层次结构而言,工作区必须是工作区根的后代。你不能也不得引用位于此文件系统层次结构之外的工作区。

    ¥Workspaces must be descendants of the workspace root in terms of folder hierarchy. You cannot and must not reference a workspace that is located outside of this filesystem hierarchy.

  • 目前不支持嵌套工作区。

    ¥Nested workspaces are not supported at this time.