管理依赖

  • 外部依赖来自 npm 注册表,使你能够利用生态系统中的宝贵代码更快地构建应用和库。

    ¥External dependencies come from the npm registry, allowing you to leverage valuable code from the ecosystem to build your applications and libraries faster.

  • 内部依赖允许你在存储库内共享功能,从而显著提高共享代码的可发现性和可用性。我们将讨论如何在 下一指南 中构建内部包。

    ¥Internal dependencies let you share functionality within your repository, dramatically improving discoverability and usability of shared code. We will discuss how to build an Internal Package in the next guide.

./apps/web/package.json
{
  "dependencies": {
    "next": "latest", // External dependency
    "@repo/ui": "workspace:*" // Internal dependency
  }
}

依赖安装的最佳实践

¥Best practices for dependency installation

在使用依赖的位置安装它们

¥Install dependencies where they're used

在存储库中安装依赖时,应将其直接安装在使用它的包中。包的 package.json 将包含包所需的所有依赖。这对于外部和内部依赖都适用。

¥When you install a dependency in your repository, you should install it directly in the package that uses it. The package's package.json will have every dependency that the package needs. This is true for both external and internal dependencies.

Good to know: 

请注意,你的包管理器可以选择 使用与包不同的 node_modules 位置

¥Note that your package manager may choose to use a different node_modules location than the package.

要快速安装多个软件包中的依赖,你可以使用软件包管理器:

¥To quickly install dependencies in multiple packages, you can use your package manager:

Terminal
pnpm add jest --save-dev --recursive --filter=web --filter=@repo/ui --filter=docs
pnpm documentation

这种做法有几个好处:

¥This practice has several benefits:

  • 清晰度提升:当包的依赖在其 package.json 中列出时,更容易理解其依赖。在代码库中工作的开发者可以一目了然地看到包中使用的依赖。

    ¥Improved clarity: It's easier to understand what a package depends on when its dependencies are listed in its package.json. Developers working in the repository can see at a glance what dependencies are used within the package.

  • 增强的灵活性:在大规模的 monorepo 中,期望每个包使用相同版本的外部依赖是不现实的。当多个团队使用同一代码库时,由于 大规模操作 的实际情况,优先级、时间表和需求会有所不同。通过在依赖的包中安装依赖,你可以让你的 ui 团队升级到最新版本的 TypeScript,而你的 web 团队可以优先发布新功能,并在稍后升级 TypeScript。此外,如果你仍想保持依赖版本同步,请使用 你也可以这样做

    ¥Enhanced flexibility: In a monorepo at scale, it can be unrealistic to expect each package to use the same version of an external dependency. When there are many teams working in the same codebase, there will be differing priorities, timelines, and needs due to the realities of operating at scale. By installing dependencies in the package that uses them, you can enable your ui team to bump to the latest version of TypeScript, while your web team can prioritize shipping new features and bumping TypeScript later. Additionally, if you still want to keep dependency versions in sync, you can do that, too.

  • 更好的缓存能力:如果你在仓库根目录中安装了太多依赖,则每次添加、更新或删除依赖时都需要更改工作区根目录,从而导致不必要的缓存未命中。

    ¥Better caching ability: If you install too many dependencies in the root of your repository, you'll be changing the workspace root whenever you add, update, or delete a dependency, leading to unnecessary cache misses.

  • 修剪未使用的依赖:对于 Docker 用户,Turborepo 的修剪功能 可以从 Docker 镜像中删除未使用的依赖,以创建更轻量的镜像。当依赖安装在其目标包中时,Turborepo 可以读取你的锁定文件并删除你所需包中未使用的依赖。

    ¥Pruning unused dependencies: For Docker users, Turborepo's pruning feature can remove unused dependencies from Docker images to create lighter images. When dependencies are installed in the packages that they are meant for, Turborepo can read your lockfile and remove dependencies that aren't used in the packages you need.

根目录中的少量依赖

¥Few dependencies in the root

按照上述第一条原则进行 在使用依赖的包中安装依赖 的部署,你会发现工作区根目录中的依赖自然会减少。

¥Following the first principle above to install dependencies in the package where they're used, you'll find that you naturally end up with few dependencies in the root of your workspace.

工作区根目录中唯一需要的依赖是用于管理存储库的工具,而用于构建应用和库的依赖则安装在各自的包中。一些适合安装在根目录中的依赖示例包括 turbohuskylint-staged

¥The only dependencies that belong in the workspace root are tools for managing the repository whereas dependencies for building applications and libraries are installed in their respective packages. Some examples of dependencies that make sense to install in the root are turbo, husky, or lint-staged.

管理依赖

¥Managing dependencies

Turborepo 不管理依赖

¥Turborepo does not manage dependencies

请注意,Turborepo 不负责管理你的依赖,这项工作由你选择的包管理器完成。

¥Note that Turborepo does not play a role in managing your dependencies, leaving that work up to your package manager of choice.

包管理器负责处理诸如下载正确的外部依赖版本、符号链接和解析模块等任务。本页上的建议是管理工作区中依赖的最佳实践,Turborepo 不强制执行。

¥It's up to the package manager to handle things like downloading the right external dependency version, symlinking, and resolving modules. The recommendations on this page are best practices for managing dependencies in a Workspace, and are not enforced by Turborepo.

不同包管理器的模块解析不同

¥Module resolution differs amongst package managers

软件包管理器有不同的模块解析算法,这会导致难以预测的行为差异。

¥Package managers have different module resolution algorithms, which leads to differences in behavior that can be difficult to predict.

在 Turborepo 文档中,我们根据包管理器的预期行为提出了许多建议。我们尽力介绍了如何处理依赖,你可能需要根据包管理器或仓库的需求调整文档中的行为。

¥In the Turborepo documentation, we make many recommendations according to the expected behaviors of the package managers. Our coverage of how to handle dependencies is best effort and you may need to adapt the documented behavior for your package manager or repository's needs.

但是,如果你发现文档中存在问题,该问题似乎对所有包管理器或特定包管理器都普遍不正确,请通过 GitHub Issue 告知我们,以便我们进行改进。

¥However, if you find an issue with the documentation that appears to be universally incorrect for all package managers or a specific one, please let us know with a GitHub Issue so we can improve.

node_modules 位置

¥node_modules locations

根据你选择的包管理器、版本、设置以及依赖在工作区中的安装位置,你可能会在工作区内的不同位置看到 node_modules 及其内部的依赖。依赖可以在根目录 node_modules 目录、包的 node_modules 目录或两者中找到。

¥Depending on your choice of package manager, version, settings, and where your dependencies are installed in your Workspace, you may see node_modules and the dependencies inside it in various locations within the Workspace. Dependencies could be found in the root node_modules, in packages' node_modules, or both.

只要你的脚本和任务能够找到所需的依赖,你的包管理器就能正常工作。

¥As long as your scripts and tasks are able to find the dependencies they need, your package manager is working correctly.

Referencing `node_modules` in your code

工作区中 node_modules 的具体位置不属于包管理器的公共 API。这意味着直接引用 node_modules(如 node ./node_modules/a-package/dist/index.js)可能很脆弱,因为依赖在磁盘上的位置可能会随着工作区周围的其他依赖的变化而变化。

¥The specific locations for node_modules within the Workspace are not a part of the public API of package managers. This means that referencing node_modules directly (like node ./node_modules/a-package/dist/index.js) can be brittle, since the location of the dependency on disk can change with other dependency changes around the Workspace.

相反,尽可能依赖 Node.js 生态系统的约定来访问依赖模块。

¥Instead, rely on conventions of the Node.js ecosystem for accessing dependency modules whenever possible.

保持依赖在同一版本

¥Keeping dependencies on the same version

一些 monorepo 维护者倾向于按照规则在所有软件包中保持对同一版本的依赖。有几种方法可以实现这一点:

¥Some monorepo maintainers prefer to keep dependencies on the same version across all packages by rule. There are several ways to achieve this:

使用专用工具

¥Using purpose-built tooling

syncpackmanypkgsherif 这样的工具可以用于此特定目的。

¥Tools like syncpack, manypkg, and sherif can be used for this specific purpose.

使用包管理器

¥Using your package manager

你可以使用软件包管理器在一个命令中更新依赖版本。

¥You can use your package manager to update dependency versions in one command.

Terminal
pnpm up --recursive typescript@latest
→ pnpm documentation

pnpm 目录

¥pnpm catalogs

在 pnpm v9.5+ 中,你可以使用目录将依赖版本范围定义为可重用常量。由于你在整个工作区中引用的是相同的值,因此这将保持依赖在同一版本上。

¥In pnpm v9.5+, you can use catalogs to define dependency version ranges as reusable constants. This will keep dependencies on the same version since you're referencing the same value across the workspace.

要了解更多信息,请访问 访问 pnpm 目录文档

¥To learn more, visit the pnpm catalogs documentation.

使用 IDE

¥Using an IDE

你的 IDE 重构工具可以一次性查找并替换代码库中所有 package.json 文件中依赖的版本。尝试在 package.json 文件上使用类似 "next": ".*" 的正则表达式来查找 next 包的所有实例,并将其替换为所需的版本。完成后,请务必运行包管理器的安装命令来更新锁定文件。

¥Your IDE's refactoring tooling can find and replace the version of a dependency across all package.json files in your repository at once. Try using a regex like "next": ".*" on package.json files to find all instances of the next package and replace them with the version you want. When you're done, make sure to run your package manager's install command to update your lockfile.

后续步骤

¥Next steps

现在你已经了解了如何在工作区中有效地管理依赖,让我们将 创建内部包 用作 monorepo 中的依赖。

¥Now that you know how to manage dependencies effectively in a workspace, let's create an Internal Package to be used as a dependency in your monorepo.