Skip to main content

工作区

什么是工作区?

¥What are workspaces?

工作区是属于同一项目的各个包的名称,Yarn 将安装并链接在一起以简化交叉引用。

¥Workspaces are the name of individual packages that are part of the same project and that Yarn will install and link together to simplify cross-references.

当与存储库结合使用时,此模式通常称为 monorepo。工作区最初由 Lerna 等项目推广,但 Yarn 是第一个为其提供原生支持的包管理器 - 随着我们围绕它们构建更多功能,多年来支持从未停止改进。

¥This pattern is often called monorepo when used in conjunction with a repository. Workspaces were initially popularized by projects like Lerna, but Yarn was the first package manager to provide native support for them - support which never stopped improving over years as we build more features around them.

信息

我们始终尝试对我们提供的功能进行测试,工作区就是一个很好的例子:Yarn 由几十个包组成,每个包都可以根据需要独立部署!

¥We always try to dogfood the features we offer, and workspaces are a prime example: Yarn is composed of a couple of dozens of packages that can each be deployed independently if needed!

我什么时候应该使用工作区?

¥When should I use workspaces?

工作区在许多情况下都很出色,主要情况是:

¥Workspaces shine in many situations, with the main ones being:

  • 当项目的核心包周围有各种附加组件时;例如,Babel 就是这种情况。

    ¥When a project has a core package surrounded with various add-ons; this is for example the case for Babel.

  • 当你需要发布多个包并希望避免你的贡献者在想要进行更改时不得不在许多单独的存储库上打开 PR 时。例如,这是 Jest 的情况。

    ¥When you need to publish multiple packages and want to avoid your contributors having to open PRs on many separate repositories whenever they want to make a change. This is for example the case for Jest.

  • 或者当项目希望在其代码中保持严格的界限并避免成为纠缠不清的巨石时。例如,对于 Yarn 本身或许多企业代码库来说就是这种情况。

    ¥Or when projects want to keep strict boundaries within their code and avoid becoming an entangled monolith. This is for example the case for Yarn itself, or many enterprise codebases.

关于 monorepos 是好是坏已经有很多讨论,双方都有合理的参数。我们的团队多年来一直使用 monorepos,并且使用 Yarn 提供的工具,其价值始终远远超过其缺点。如果你需要为你的项目创建另一个包,请考虑将它们分组在一起是否有意义。

¥There's been a significant amount of discussions about whether monorepos are good or bad, with decent arguments on both side. Our team worked with monorepos for years at this point, and with the tooling Yarn provides, the value has always outweighed the cons by a large margin. If you need to create another package for your project, consider whether grouping them together makes sense.

提示

你无需将代码拆分到多个工作区中才能使用它们。例如,Clipanion 存储库 仅使用两个工作区,一个用于库,一个用于其网站。此模式避免了混合依赖,同时也使编写影响代码和文档的 PR 变得容易。

¥You don't need to split your code in many workspaces for them to become useful. For instance, the Clipanion repository uses only two workspaces, one for the library and one for its website. This patterns avoids mixing dependencies while also making it easy to author PRs that affect both the code and the documentation.

如何声明工作区?

¥How are workspaces declared?

要声明工作区,你所要做的就是将 workspaces 数组添加到根 package.json 文件,并列出指向工作区文件夹的相对 glob 模式。在以下示例中,packages 文件夹中的所有子目录都将成为工作区。

¥To declare a workspace, all you have to do is add a workspaces array to the root package.json file, and list relative glob patterns pointing to your workspaces' folders. In the following example, all subdirectories in the packages folder will become workspaces.

{
"workspaces": [
"packages/*"
]
}

¥Workspace-related features

约束

¥Constraints

约束之于 monorepo 就像 Eslint 之于源代码。它们允许你声明必须应用于项目中特定工作区的规则。例如,你可以使用约束来强制同步项目中的所有依赖,以防止使用某些依赖,或者强制在任何地方正确设置某些字段(例如 licenseengines.node)。

¥Constraints are to monorepos what Eslint is to your source code. They let you declare rules that must apply to specific workspaces in your project. For example, you can use constraints to enforce that all dependencies in your project are synchronized, to prevent some dependencies from being used, or to enforce that some fields such as license or engines.node are properly set everywhere.

有关约束的更多信息和示例,请查看 专用文章

¥For more information and examples about constraints, check the dedicated article.

交叉引用

¥Cross-references

来自 monorepos 的包通常需要相互依赖 - 例如当你有一个依赖于单独库的应用包时。由于特殊的 workspace: 协议,Yarn 使安装变得非常容易,该协议允许你指示 Yarn 使用项目中同名的工作区来解决依赖。例如:

¥Packages from monorepos often need to depend on each other - for example when you have an app package depending on a separate library. Yarn makes it very easy thanks to the special workspace: protocol, which lets you instruct Yarn to resolve the dependency using the workspace of the same name in the project. For example:

{
"dependencies": {
"@my-org/utils": "workspace:^"
}
}

workspace: 协议接受常规 semver 范围或特殊的 ^ / ~ / * 令牌。无论值是什么,都不会改变 Yarn 解析依赖的方式(它只会关心依赖名称),但它会影响通过 yarn npm publish 发布包后依赖的样子。例如,如果针对 version 字段设置为 3.2.1 的工作区使用以下范围:

¥The workspace: protocol accepts either a regular semver range, or the special ^ / ~ / * tokens. Whatever the value is won't change how Yarn will resolve the dependency (it will only ever care about the dependency name), but it will affect what the dependency will look like after the package gets published via yarn npm publish. For example, if the following ranges are used against a workspace whose version field is set to 3.2.1:

初始范围发布后的范围
workspace:^3.0.0^3.0.0
workspace:^^3.2.1
workspace:~~3.2.1
workspace:*=3.2.1

专注安装

¥Focused installs

发现工作区时常见的问题是,每当你想要处理其中一个工作区时,你需要如何安装它们的所有依赖。Yarn 通过 yarn workspaces focus 提供解决方案。

¥A common concern when discovering workspaces is how you need to install all of their dependencies whenever you wish to work on a single one of them. Yarn provides a solution via yarn workspaces focus.

此命令获取工作区列表,扩展列表以包含传递依赖,并从安装中排除其他所有内容。例如,以下内容将允许你仅安装构建和部署主应用所需的依赖:

¥This command takes a list of workspaces, extend the list to include transitive dependencies, and exclude everything else from the install. For example, the following would let you install only the dependencies required for your main app to be built and deployed:

yarn workspaces focus @my-org/app

如果你还希望跳过安装 devDependencies,请设置 --production 标志。在以下示例中,Yarn 将安装所有工作区的依赖,但仅安装生产工作区的依赖:

¥If you wish to also skip installing devDependencies, set the --production flag. In the following example, Yarn will install the dependencies from all workspaces, but only the production ones:

yarn workspaces focus -A --production

全局脚本

¥Global scripts

可以使用 yarn run name 运行来自 package.json 文件的 scripts 字段中定义的脚本,但前提是你从声明它们的工作区内运行命令。也就是说,除非它们是全局脚本。

¥Scripts defined in the scripts field from the package.json files can be run using yarn run name, but only if you run the command from within the workspaces that declare them. That is, unless they are global scripts.

全局脚本的特点是脚本名称中至少有一个冒号 (:)。它们可以在项目内的任何位置运行,只要没有重复(如果两个工作区定义了具有相同名称的脚本,则它们不会升级为全局脚本)。

¥Global scripts are characterized by at least one colon (:) in the script name. They can be run from anywhere within the project, as long as there are no duplicates (if two workspaces define scripts with the same names, they won't be upgraded into global scripts).

并行执行

¥Parallel execution

如果多个工作区的脚本共享相同的名称,则可以通过使用 yarn workspaces foreach 并行运行。以下示例向你展示了如何并行发布项目中的所有包,但要遵守拓扑顺序(以便工作区仅在其所依赖的所有其他工作区都发布后才会发布):

¥Scripts from multiple workspaces can be run in parallel if they share the same name, by using yarn workspaces foreach. The following example shows you how to publish all packages in your project in parallel, but respecting topological order (so that a workspace only gets published once all other workspaces it depends on did):

yarn workspaces foreach --all -pt npm publish

--all 标志将在项目中的每个工作区上运行提供的命令,但可以对其进行调整。在此示例中,我们使用 --since 标志来仅选择与 主分支 相比在当前分支中修改的工作区:

¥The --all flag will run the provided command on every workspace in the project, but it can be tweaked. In this example we use the --since flag to instead only select workspaces that were modified in the current branch compared to the main branch:

yarn workspaces foreach --since run lint

类似地,--from pattern 标志将选择与提供的 glob 模式匹配的所有工作区。对于所有其他 Yarn 命令,此标志将应用于工作区名称和相对于当前工作目录的路径。例如,此命令将在当前工作区及其所依赖的所有其他工作区上运行 build 脚本:

¥Similarly, the --from pattern flag will instead select all workspaces matching the provided glob pattern. As for all other Yarn commands, this flag will be applied to both workspace names and paths relative to the current working directory. For example, this command will run the build script on the current workspace and all other workspaces it depends on:

yarn workspaces foreach --from . -R run build