创建新的单仓库
本指南使用 turbo
的全局安装。请遵循 安装指南 来完成此设置。或者,你可以在下面的命令中使用你的包管理器来运行本地安装的 turbo
。
快速入门
要创建一个新的单仓库,请使用我们的 create-turbo
(在新标签页打开) npm 包
npx create-turbo@latest
你也可以克隆一个 Turborepo 启动器仓库来为你的单仓库快速上手。要查看 Turborepo 示例和启动器,请参阅 GitHub 上的 Turborepo 示例目录 (在新标签页打开).
完整教程
本教程将引导你设置一个基本示例。到最后,你将对使用 turbo
感到自信,并了解所有基本功能。
在本教程中,代码示例中省略了一些代码行。例如,在显示 package.json
时,我们不会显示所有键 - 只有重要的键。
1. 运行 create-turbo
首先,运行
npx create-turbo@latest
这将安装 create-turbo
(在新标签页打开) CLI,并运行它。你将被问到几个问题
你想在哪个位置创建你的 turborepo?
选择任何你喜欢的位置。默认位置是 ./my-turborepo
。
你想使用哪个包管理器?
Turborepo 不处理包安装,因此你需要选择以下其中一个
create-turbo
将检测你的系统上可用的包管理器。如果你不确定选择哪个,Turborepo 建议使用 pnpm
。
安装
选择包管理器后,create-turbo
将在您选择的文件夹名称中创建一堆新文件。它还会安装默认情况下随 basic
示例一起提供的依赖项。
2. 探索您的新仓库
您可能在终端中注意到了一些东西。 create-turbo
给您提供了它正在添加的所有内容的描述。
>>> Creating a new turborepo with the following:
- apps/web: Next.js with TypeScript
- apps/docs: Next.js with TypeScript
- packages/ui: Shared React component library
- packages/eslint-config: Shared configuration (ESLint)
- packages/typescript-config: Shared TypeScript `tsconfig.json`
这些都是工作区 - 包含一个 package.json
的文件夹。每个工作区都可以声明自己的依赖项,运行自己的脚本,并导出代码供其他工作区使用。
打开根文件夹 - ./my-turborepo
- 在您最喜欢的代码编辑器中。
了解 packages/ui
首先,打开 ./packages/ui/package.json
。您会注意到该包的名称是 "name": "@repo/ui"
- 位于文件的最顶部。
接下来,打开 ./apps/web/package.json
。您会注意到此包的名称是 "name": "web"
。但也要看看它的依赖项。
您会看到 "web"
依赖于一个名为 "@repo/ui"
的包。
{
"dependencies": {
"@repo/ui": "*"
}
}
这意味着我们的网络应用程序依赖于我们的本地 @repo/ui
包。
如果您查看 apps/docs/package.json
,您会看到相同的内容。 web
和 docs
都依赖于 @repo/ui
- 一个共享的组件库。
这种在应用程序之间共享代码的模式在单体仓库中非常常见 - 这意味着多个应用程序可以共享一个设计系统。
了解导入和导出
查看 ./apps/docs/app/page.tsx
内部。 docs
和 web
都是 Next.js (opens in a new tab) 应用程序,它们都以类似的方式使用 @repo/ui
库
import { Button } from "@repo/ui/button";
// ^^^^^^ ^^^^^^^^^^^^^^^
export default function Page() {
return (
<>
<Button appName="web" className={styles.button}>
Click me!
</Button>
<>
);
}
它们从名为 @repo/ui/button
的依赖项中直接导入 Button
!这是怎么工作的? Button
来自哪里?
打开 packages/ui/package.json
。您会注意到 exports
字段
{
"exports": {
"./button": "./src/button.tsx",
"./card": "./src/card.tsx",
"./code": "./src/code.tsx"
},
}
当工作区从 @repo/ui/button
导入时, exports
会告诉它们在哪里访问它们正在导入的代码。
所以,让我们看看 packages/ui/src/button.tsx
内部
"use client";
import { ReactNode } from "react";
interface ButtonProps {
children: ReactNode;
className?: string;
appName: string;
}
export const Button = ({ children, className, appName }: ButtonProps) => {
return (
<button
className={className}
onClick={() => alert(`Hello from your ${appName} app!`)}
>
{children}
</button>
);
};
我们找到了我们的按钮!
此文件中的所有内容都可以被依赖于 @repo/ui/button
的工作区使用。
我们在此文件中进行的任何更改都将在 web
和 docs
中共享。太酷了!
尝试从该文件中导出不同的函数。也许是 add(a, b)
用于将两个数字加在一起。
然后, web
和 docs
可以导入它。
了解 tsconfig
我们还有两个工作区要查看, typescript-config
和 eslint-config
。这些都允许在整个单体仓库中共享配置。让我们看看 typescript-config
{
"name": "@repo/typescript-config",
}
在这里我们看到包的名称是 @repo/typescript-config
。
现在,让我们看看位于我们的 web
应用程序中的 tsconfig.json
文件。
{
"extends": "@repo/typescript-config/nextjs.json",
}
如您所见,我们正在将 @repo/typescript-config/nextjs.json
直接导入到我们的 tsconfig.json
文件中。
这种模式允许单体仓库在所有工作区中共享一个 tsconfig.json
,从而减少代码重复。
了解 eslint-config
我们最后一个工作区是 eslint-config
。
让我们从查看 packages/eslint-config/package.json
内部开始
{
"name": "@repo/eslint-config",
"files": [
"library.js",
"next.js",
"react-internal.js"
],
}
如您所见,该包名为 @repo/eslint-config
,它公开了三个文件: library.js
、 next.js
和 react-internal.js
。
要了解如何使用自定义 ESLint 配置,让我们看看 apps/docs/.eslintrc.js
内部
module.exports = {
extends: ["@repo/eslint-config/next.js"],
};
在这里您可以看到,我们正在将 @repo/eslint-config/next.js
直接导入到我们的 .eslintrc.js
文件中。
就像 typescript-config
一样, eslint-config
让我们在整个单体仓库中共享 ESLint 配置,无论您在哪个项目上工作,都能保持一致性。
总结
了解这些工作区之间的依赖关系很重要。让我们将它们映射出来
web
- 依赖于ui
,typescript-config
和eslint-config
docs
- 依赖于ui
,typescript-config
和eslint-config
ui
- 依赖于typescript-config
和eslint-config
typescript-config
- 无依赖eslint-config
- 无依赖
请注意,**Turborepo CLI 不负责管理这些依赖项**。以上所有内容都由您选择的包管理器(npm
, pnpm
或 yarn
)处理。
3. 了解 turbo.json
现在我们了解了我们的仓库及其依赖关系。Turborepo 如何提供帮助?
Turborepo 通过简化任务运行并使其更加高效来提供帮助。
让我们看一下根目录下的 turbo.json
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**"]
},
"lint": {
"dependsOn": ["^lint"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}
我们在这里看到的是,我们已经使用 turbo
注册了三个任务:lint
, dev
和 build
。在 turbo.json
中注册的每个任务都可以使用 turbo run <task>
(或简写为 turbo <task>
)运行。
在我们继续之前,让我们尝试运行一个名为 hello
的任务,该任务未在 turbo.json
中注册
turbo hello
您将在终端中看到错误。类似于
Could not find the following tasks in project: hello
值得记住的是 - **为了使 turbo
运行任务,它必须在 turbo.json
中**。
让我们调查一下我们已经设置的脚本。
4. 使用 Turborepo 进行代码风格检查
尝试运行我们的 lint
脚本
turbo lint
您将在终端中注意到发生了一些事情。
- 几个脚本将同时运行,每个脚本都以
docs:lint
,@repo/ui:lint
或web:lint
为前缀。 - 它们将全部成功,您将在终端中看到
3 successful
。 - 您还将看到
0 cached, 3 total
。我们将在后面介绍这意味着什么。
每个运行的脚本都来自每个工作区的 package.json
。每个工作区可以选择指定自己的 lint
脚本
{
"scripts": {
"lint": "next lint"
}
}
{
"scripts": {
"lint": "next lint"
}
}
{
"scripts": {
"lint": "eslint \"**/*.ts*\""
}
}
当我们运行 turbo lint
时,Turborepo 会查看每个工作区中的每个 lint
脚本并运行它。有关更多详细信息,请参阅我们的 管道 文档。
使用缓存
让我们再次运行我们的 lint
脚本。您将在终端中注意到一些新内容出现
cache hit, replaying logs
出现在docs:lint
,web:lint
和@repo/ui:lint
中。- 您将看到
3 cached, 3 total
。 - 总运行时间应该在
100ms
以下,并且>>> FULL TURBO
会出现。
刚刚发生了一些有趣的事情。Turborepo 意识到,**我们的代码自上次运行代码风格检查脚本以来没有发生变化**。
它保存了上次运行的日志,因此它只是重播了它们。
让我们尝试更改一些代码以查看会发生什么。对 apps/docs
中的文件进行更改
import { Button } from "@repo/ui/button";
// ^^^^^^ ^^^^^^^^^^^^^^^
export default function Page() {
return (
<>
<Button appName="web" className={styles.button}>
- Click me!
+ Click me now!
</Button>
<>
);
}
现在,再次运行 lint
脚本。您会注意到
docs:lint
有一条评论说cache miss, executing
。这意味着docs
正在运行其代码风格检查。2 cached, 3 total
出现在底部。
这意味着 **我们之前任务的结果仍然被缓存了**。只有 docs
中的 lint
脚本实际上运行了 - 再次加快了速度。要了解更多信息,请查看我们的 缓存文档。
5. 使用 Turborepo 进行构建
让我们尝试运行我们的 build
脚本
turbo build
您将看到与运行 lint 脚本时类似的输出。只有 apps/docs
和 apps/web
在其 package.json
中指定了 build
脚本,因此只运行这些脚本。
查看 turbo.json
中的 build
部分。那里有一些有趣的配置。
{
"pipeline": {
"build": {
"outputs": [".next/**", "!.next/cache/**"]
}
}
}
您会注意到已指定了一些 outputs
。声明输出意味着当 turbo
完成运行您的任务时,它会将您指定的输出保存到其缓存中。
apps/docs
和 apps/web
都是 Next.js 应用程序,它们将构建输出到 ./.next
文件夹。
让我们尝试一下。删除 apps/docs/.next
构建文件夹。
再次运行 build
脚本。您会注意到
- 我们遇到了
FULL TURBO
- 构建在不到100ms
内完成。 .next
文件夹重新出现!
Turborepo 缓存了我们之前构建的结果。当我们再次运行 build
命令时,它从缓存中恢复了整个 .next/**
文件夹。要了解更多信息,请查看我们关于 缓存输出 的文档。
6. 运行开发脚本
现在让我们尝试运行 dev
。
turbo dev
您会注意到终端中的一些信息
- 只有两个脚本将执行 -
docs:dev
和web:dev
。这是唯一两个指定了dev
的工作区。 - 两个
dev
脚本同时运行,在端口3000
和3001
上启动您的 Next.js 应用程序。 - 在终端中,您将看到
cache bypass, force executing
。
尝试退出脚本,然后重新运行它。您会注意到我们没有进入 FULL TURBO
。这是为什么呢?
查看 turbo.json
{
"pipeline": {
"dev": {
"cache": false,
"persistent": true
}
}
}
在 dev
中,我们指定了 "cache": false
。这意味着我们告诉 Turborepo *不要*缓存 dev
脚本的结果。 dev
运行一个持久化的开发服务器,并且不产生任何输出,因此没有可缓存的内容。在我们的文档中了解更多关于 关闭缓存 的信息。
此外,我们设置了 "persistent": true
,以让 turbo 知道这是一个长时间运行的开发服务器,以便 turbo 可以确保没有其他任务依赖于它。您可以在 persistent
选项的文档 中了解更多信息。
一次只在一个工作区上运行 dev
默认情况下, turbo dev
将在所有工作区上同时运行 dev
。但有时,我们可能只想选择一个工作区。
为了处理这种情况,我们可以向我们的命令添加一个 --filter
标志。
turbo dev --filter docs
您会注意到它现在只运行 docs:dev
。从我们的文档中了解更多关于 过滤工作区 的信息。
总结
做得好!您已经了解了您的新单体仓库,以及 Turborepo 如何让处理您的任务变得更容易。
下一步
- 需要添加更多任务吗?了解有关使用 管道 的更多信息
- 想要加快您的 CI 速度吗?设置 远程缓存。
- 想要一些灵感吗?查看我们的 示例 (opens in a new tab) 目录。