Open WangShuXian6 opened 1 month ago
如果你曾参与过任何规模的 JavaScript 项目,你可能都遇到过管理依赖项和持续集成过程的压力。随着项目的扩大和团队中工程师的增加,这些问题会变得更加复杂。这时,单一代码库(Monorepo)架构便能发挥作用。通过集中代码,你可以简化开发过程并简化项目管理。在本课程中,我们将探索如何使用 JavaScript 和 TypeScript 构建可扩展的单一代码库。我们将从 pnpm 和 pnpm 工作区的介绍开始,随后探讨如何使用 Turborepo 管理大型项目,以及如何配置这些项目以实现最高效率和性能。那么,让我们开始吧,构建我们自己的可扩展单一代码库!
在进入课程之前,我们先聊一聊在学习单一代码库(monorepos)之前需要具备的知识。本课程中我们会用到一些 JavaScript 和 TypeScript。你不需要对这两者非常精通,但这是我们将使用的环境。我们还希望你对 Node.js 和包管理器有一定的了解,比如安装文件的基本操作等。我们会介绍 pnpm 作为管理这些包的工具,所以如果你对此不熟悉也没关系。
拥有一个代码编辑器也会很有帮助。我将使用 VS Code,但你可以选择任何你喜欢的编辑器,比如 Sublime Text 或 Atom 等。你只需要一个可以打开和运行文件的地方。好了,这就是我们开始所需要的一切。接下来的视频中,我们将一起开始构建项目。
在我们实际构建自己的单一代码库(monorepo)之前,先聊聊为什么你可能会选择这种方式。
在多代码库中,每个应用或服务都保存在其各自的独立仓库中。
例如,我们的主仓库在这里是仓库 1,并且会有单独的服务或库,比如身份验证系统、设计系统、组件库、表单等,所有这些都会保存在各自的仓库中。然后,这些共享组件会发布到像 npm 这样的包管理器,以便其他项目可以引用它们。因此,从仓库 1 中,我们可以通过 npm 安装组件库,并在组件库更新时将其更新到主应用中。
单一代码库是一个集中存放多个项目代码的仓库,通常包括相关的 JavaScript 或 TypeScript 包。这些项目既独立又互相连接,范围可以从底层的工具到高级的 Web 用户界面。这里的唯一不同之处在于我们如何存放代码。组织成小型、独立、自包含的组件,并定义良好的边界和接口依旧是一个好主意。
统一代码库:所有项目和组件都在一个地方,这使得代码库的管理和访问更加便捷,简化了组织中的导航和代码搜索。
共享依赖:共享依赖可以确保一致性并减少重复,从而实现更高效的构建和更小的总体依赖负担。
一致的工具链:在同一个框架下进行测试、构建和部署,使所有项目使用相同的工具,便于在各项目间实现一致性。
提升协作:所有代码集中在一个地方,开发人员可以更轻松地在团队和项目间进行协作,有助于实现更加紧密的产品开发体验。
这就是我们将要构建的目标:一个能容纳多个不同项目的单一代码库。
在我们开始之前,确保已安装 Node.js。我们将检查其版本,通过运行 node -v
可以查看版本,我们需要的是版本 20 或更高。如果出现错误或版本低于 18,可以访问 nodejs.org 并按照安装说明进行设置。
在本课程中,我们还会使用一个叫做 pnpm 的工具。pnpm 是一个快速、高效的 JavaScript 包管理器,是 npm 的替代方案,并且更适合单一代码库(monorepo)工具和更高效的包管理。因此,我们需要确保已安装 pnpm。
https://pnpm.io/ 在 Windows 上可以使用 IWR 命令进行安装,如果是在 Docker 容器中,也有多种安装方式。你可以通过 npm、Homebrew 等工具进行安装。这里我将使用 Homebrew 进行安装。回到终端,输入以下命令:
brew install pnpm
这将进行安装,以便我们在所有不同的项目中全局使用 pnpm。安装过程中可能会提示我们需要通过 brew 安装 Node.js,这没关系,可以继续执行:
brew install node
安装完成后,再次运行 brew install pnpm
。如果你已经有旧版本的 pnpm,也可以重新安装它。接下来,运行以下命令来检查 pnpm 的版本:
pnpm --version
输出应为 8.9.0 或更高版本,9.3、9.6 等都是可以的。
总结一下,pnpm 是 npm 的替代工具,更快速、高效的包管理器。我们将使用它来设置我们的单一代码库(monorepo)。
让我们使用 pnpm 创建项目。我已在 VS Code 中打开了终端窗口,并确保导航到 01_03b
目录下。在项目根目录下运行 pnpm init
,这会创建一个带有默认值的 package.json
文件。
接着,创建一个名为 packages
的目录。然后在其中再创建一个名为 snowtooth-one
的目录,它将成为单一代码库中的一个应用。导航到 packages/snowtooth-one
目录,并在这里运行 pnpm init
,这会创建该应用的 package.json
文件。
在 snowtooth-one
文件夹中创建一个 index.js
文件,编写以下代码:
function calculateElevation(feet) {
const meters = feet * 0.3048;
return Math.round(meters);
}
const verticalFeet = 12000;
console.log(`The resort has an elevation of ${calculateElevation(verticalFeet)} meters.`);
接下来,打开 snowtooth-one
文件夹内的 package.json
文件,替换其中的 scripts
部分,加上 start
命令:
"scripts": {
"start": "node index.js"
}
这样,每当我运行 pnpm run start
时,它将会启动 Node.js 运行 index.js
文件。现在,我们在终端中运行 pnpm run start
,可以看到输出的海拔高度以米为单位显示出来。
接下来,我们希望利用单一代码库结构。进入项目根目录 01_03b
的 package.json
文件,添加以下命令:
"scripts": {
"snowtooth-one:start": "pnpm -r --filter @snowtooth-mountain/snowtooth-one run start"
}
其中,-r
参数让 pnpm 在单一代码库中的每个包中递归运行命令,--filter
参数用于指定包名 @snowtooth-mountain/snowtooth-one
。
在根目录 01_03b
中,将其名字替换为 snowtooth-mountain
。现在回到根目录运行:
pnpm run snowtooth-one:start
该命令将进入 snowtooth-one
文件夹并运行 node index.js
,结果会显示我们的度假村的海拔高度为 3658 米。
现在让我们在项目中创建另一个包。首先,复制 snowtooth-one
文件夹,并将其粘贴到 packages
目录中。复制的文件夹将命名为 snowtooth-one copy
,我们将其重命名为 snowtooth-two
。
接着,将原有的 calculateElevation
函数替换为新的函数 calculateHikingTime
。新函数接收 trailLength
和 avgSpeed
参数,用于计算徒步时间。代码如下:
function calculateHikingTime(trailLength, avgSpeed) {
return trailLength / avgSpeed;
}
const length = 10;
const avgSpeed = 3;
console.log(`It would take approximately ${Math.round(calculateHikingTime(length, avgSpeed))} hours to hike the trail.`);
将以上代码粘贴到 snowtooth-two
文件夹中的 index.js
文件中。然后,打开 snowtooth-two
文件夹中的 package.json
文件,替换 scripts
部分,以便可以使用 start
命令:
"scripts": {
"start": "node index.js"
}
在项目根目录的 package.json
文件中,添加以下命令以便可以在根目录中运行 snowtooth-two
包的脚本:
"scripts": {
"snowtooth-one:start": "pnpm -r --filter @snowtooth-mountain/snowtooth-one run start",
"snowtooth-two:start": "pnpm -r --filter @snowtooth-mountain/snowtooth-two run start"
}
确保在适当的目录结构下,并在终端中运行以下命令来启动 snowtooth-two
包:
pnpm run snowtooth-two:start
这将输出徒步所需的时间,大约为 3 小时。现在,我们有了两个不同的包可以正常运行。在接下来的视频中,我们将探讨如何创建 pnpm 工作区以管理这两个包。
现在我们有了两个独立的包,每个包都有自己的 package.json
和 start
脚本。接下来,我们将创建一个新脚本,用于同时运行这两个包。
目前,我们可以在根目录运行其中一个脚本,例如:
pnpm run snowtooth-one:start
或者:
pnpm run snowtooth-two:start
但为了方便,我们希望能够同时运行这两个脚本。为此,我们将创建一个可以同时运行两个包的 start
脚本。具体操作如下:
在根目录的 package.json
文件中,添加以下脚本:
"scripts": {
"start": "pnpm -r run start"
}
这里的 -r
参数表示递归运行,即会在单一代码库(monorepo)中的每个包内运行指定的命令。
在项目根目录下,创建一个名为 pnpm-workspace.yaml
的新文件,用于定义工作区。内容如下:
packages:
- "packages/**"
这段配置将告诉 pnpm 在 packages
文件夹中查找所有嵌套的包。
完成以上步骤后,可以在根目录运行以下命令:
pnpm run start
这会同时运行我们两个包的 start
脚本。输出会显示当前在两个工作区项目中运行脚本。
通过这种方式,我们可以递归运行 monorepo 中的命令,从而更方便地管理多个包,将它们视为一个整体来操作。
现在我们有两个不同的包,各自包含了可以在项目中跨包使用的辅助函数。在 snowtooth-one
包中有 calculateElevation
函数,而在 snowtooth-two
包中有 calculateHikingTime
函数。假设我们想在 snowtooth-one
中使用 calculateHikingTime
函数,可以通过以下步骤实现跨包导入。
在每个包的 package.json
中添加以下内容,以将其声明为 ECMAScript 模块:
"type": "module"
snowtooth-two
中的函数在 snowtooth-two
中确保导出 calculateHikingTime
函数:
export function calculateHikingTime(trailLength, avgSpeed) {
return trailLength / avgSpeed;
}
snowtooth-one
中导入函数接下来,在 snowtooth-one
中的代码中导入 calculateHikingTime
函数:
import { calculateHikingTime } from "@snowtooth-mountain/snowtooth-two";
在 snowtooth-one
的 package.json
文件中,添加依赖项:
"dependencies": {
"@snowtooth-mountain/snowtooth-two": "workspace:*"
}
"workspace:*"
告诉 pnpm 查找在 pnpm-workspace.yaml
文件中定义的工作区路径下的依赖包。
导航到 snowtooth-one
目录并运行以下命令以安装依赖:
pnpm install
然后回到根目录,并运行以下命令以启动项目:
pnpm run start
运行结果会在控制台显示两个包的输出。如果 calculateHikingTime
函数成功导入并运行,则表示跨包引用已正确配置并生效。这就是在 Monorepo 结构中跨包使用代码的流程!
现在,当我们导航到 02_02b
并运行 pnpm start
时,我们可以成功导入 calculateHikingTime()
函数,并将其导出到另一个包中。
接下来,我们将完成相反的操作:把 calculateElevation
函数导出并在 snowtooth-two
中导入。具体任务如下:
导出 calculateElevation
函数:
在 snowtooth-one
的 index.js
文件中,确保 calculateElevation
函数被导出:
export function calculateElevation(feet) {
const meters = feet * 0.3048;
return Math.round(meters);
}
在 snowtooth-two
中导入该函数:
在 snowtooth-two
的 index.js
文件中,导入 calculateElevation
函数:
import { calculateElevation } from "@snowtooth-mountain/snowtooth-one";
运行 pnpm run start
:
当运行 pnpm run start
时,确保在 snowtooth-two
的 index.js
文件中调用 calculateElevation
,以便我们可以看到两个不同的 console.log
输出。这样在运行 snowtooth-two
的索引文件时,应该能看到两个函数的日志输出。
试试看!在下一个视频中,我将展示解决方案。
现在让我们实现跨包的反向导入。我们已将 calculateElevation
函数从 snowtooth-one
导出,接下来要在 snowtooth-two
中导入它。步骤如下:
在 snowtooth-one
中,确保 calculateElevation
函数已被导出:
export function calculateElevation(feet) {
const meters = feet * 0.3048;
return Math.round(meters);
}
snowtooth-two
中导入打开 snowtooth-two
的 index.js
文件,并添加导入语句:
import { calculateElevation } from "@snowtooth-mountain/snowtooth-one";
然后在代码中调用 calculateElevation
函数,以便能够使用它。
在 snowtooth-two
的 package.json
文件中,确保添加对 @snowtooth-mountain/snowtooth-one
的依赖:
"dependencies": {
"@snowtooth-mountain/snowtooth-one": "workspace:*"
}
这表示依赖来自于工作区内定义的包。
导航到 snowtooth-two
目录,运行以下命令安装依赖:
pnpm install
返回项目根目录,并运行以下命令:
pnpm start
此时,终端应显示两个包的日志输出,分别调用了 calculateElevation
和 calculateHikingTime
函数。这些步骤使得我们可以在不同项目和包中重用代码,从而提高代码复用性并确保应用的高性能。
另一种引用包的方法是通过文件路径进行引用,而不是使用 workspace:*
。我们可以通过指定路径来引用 snowtooth-one
包。以下是具体步骤:
node_modules
文件夹首先,如果 snowtooth-two
中存在 node_modules
文件夹,请确保将其删除,以便清除现有依赖项。
snowtooth-one
包在 snowtooth-two
的 package.json
文件中,将 @snowtooth-mountain/snowtooth-one
的依赖路径替换为相对路径,例如:
"dependencies": {
"@snowtooth-mountain/snowtooth-one": "file:../snowtooth-one"
}
这将直接指向本地 snowtooth-one
包的文件路径。
保存更改后,在 snowtooth-two
目录中运行以下命令:
pnpm install
这会安装该依赖项并确保 snowtooth-two
正确引用了 snowtooth-one
包。
为了验证路径引用是否正常工作,可以尝试使用一个不存在的路径,例如将依赖路径更改为 Pancake
,然后再次运行 pnpm install
。这会提示警告,因为引用路径无效。此时运行 pnpm start
,你会看到一系列错误,因为路径找不到对应的文件。
将路径修复为正确的 ../snowtooth-one
,然后再次删除 node_modules
文件夹并重新安装依赖项:
pnpm install
最后,在根目录运行:
pnpm start
现在应该能够正常启动。这是一种引用本地文件的方式。选择这种方法时,只需确保路径正确指向所需的文件即可。
一个很好的开发工具是 Turbo。你可能听说过它的其他名称,例如 Turbo Repo 或 Turbo Pack。Turbo Repo 是一个用于 JavaScript 和 TypeScript 项目的构建系统,而 Turbo Pack 是一个 Beta 项目,专门用于服务器组件和 TypeScript 代码库。
Turbo Repo 和 Turbo Pack 都是用 Rust 编写的,目标是实现高性能,确保项目构建快速。它们的默认结构适用于单一代码库(monorepo)。如果你正在进行一个 monorepo 项目,并希望使用 Vercel 的开发工具(例如 Next.js、AI SDK 等),可以考虑使用 Turbo。
我们将在项目中安装 Turbo,确保处于正确的目录 0301B
下,运行以下命令:
npx create-turbo@latest
或者 生成带Vite的项目目录 https://turbo.build/repo/docs/guides/frameworks/vite
npx create-turbo@latest -e with-vite
这个命令会创建一个新项目。我们将其命名为 Snow Tooth,选择 PNPM 作为包管理器,这样就会生成一个 Snow Tooth
文件夹。
新创建的项目包含以下几个文件夹和文件:
ESLint
配置、TypeScript
配置,以及一些 React 组件(如按钮和卡片)组成的 UI 库。这种结构是启动项目的快捷方式。在下一个视频中,我们将探讨如何将现有文件结构与 PNPM 和 Turbo Repo 结合使用。
snowtooth\pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
在上一个视频中,我们生成了一个包含多个应用和内部包的项目,包括一个文档站点和一个网站应用,以及 eslint-config
、typescript-config
等包。需要注意的是,每个应用和包都拥有独立的 package.json
文件,而整个项目也有一个全局的 package.json
文件。我们在不同的包中安装依赖项,遵循一个基本原则:在使用依赖的包中直接安装它们,而不是安装在全局项目中。
假设我们想在 docs
项目中添加 Jest(一个 React 测试库):
确保当前位于 docs
目录下,运行以下命令将 Jest 添加为开发依赖:
pnpm install jest --save-dev
这样 Jest 就被安装到 docs
项目的 package.json
中,作为开发依赖。
递归安装依赖: 如果我们希望在多个项目中安装 Jest,可以回到根目录并使用递归安装。示例如下:
pnpm install jest --save-dev --recursive --filter=web --filter=@repo/ui
--recursive
表示浏览嵌套文件夹
该命令将 Jest 安装到 web
和 @repo/ui
目录下的 package.json
文件中,作为开发依赖。--filter
参数指定了具体安装的文件夹或包。
这种安装依赖的方法遵循了最佳实践,即在使用的地方安装依赖。这有助于保持依赖的清晰性、提高缓存能力,并在大型项目中更灵活地管理依赖。
在此示例中,通过递归安装,我们能够在单一代码库的多个文件夹中有效地添加开发依赖,并确保项目结构合理、依赖安装明确。
现在我们学习了如何安装外部依赖项,接下来将创建一个内部依赖项,以便将项目内的不同包相互连接。内部包是应用的基础构件,比如 eslint-config
、typescript-config
和 UI
等包,这些包可以被其他应用程序使用,就像一个内部的 NPM 包注册库。
创建新目录:在 packages
文件夹中创建一个新目录 elevation
。
初始化 package.json
:导航到 elevation
目录下,并运行以下命令:
pnpm init
这会在 elevation
目录下生成一个 package.json
文件。
配置 package.json
:
在 package.json
中添加以下内容:
Scripts:
"scripts": {
"dev": "tsc --watch",
"build": "tsc"
}
dev
脚本将启动 TypeScript 编译器并监视文件变化,build
脚本用于编译 TypeScript 文件。
开发依赖:
添加 @repo/typescript-config
和 typescript
为开发依赖:
"devDependencies": {
"@repo/typescript-config": "workspace:*",
"typescript": "latest"
}
包名称和模块类型:
为保持一致性,将包命名为 @repo/elevation
,并指定模块类型为 ES 模块:
"name": "@repo/elevation",
"type": "module"
安装依赖:
在 elevation
目录下运行以下命令来安装依赖:
pnpm install
该命令会安装 @repo/typescript-config
和最新版本的 TypeScript,以便我们在 elevation
包中使用这些配置。
完成这些步骤后,我们的 elevation
包已配置完毕,并可在整个单一代码库中使用。在下一节中,我们将设置 TypeScript 配置并编写该包的源代码。
现在我们已经创建了 elevation
包,接下来需要创建源文件目录并编写我们要导出的函数。
src
文件夹在 elevation
目录下,创建一个名为 src
的新文件夹,然后在 src
文件夹中创建一个名为 calculateElevation.ts
的 TypeScript 文件。
calculateElevation
函数在 calculateElevation.ts
文件中,编写以下函数并添加 TypeScript 类型注解:
function calculateElevation(feet: number): number {
const meters: number = feet * 0.3048;
return Math.round(meters);
}
// 导出函数
export { calculateElevation };
此函数将接收一个 feet
参数,并返回一个以米为单位的值。
package.json
文件接下来,需要在 elevation
包的 package.json
中定义导出入口,以便其他包可以导入该函数。
打开 package.json
,添加一个 exports
字段来定义包的入口:
"exports": {
"./calculateElevation": {
"types": "./src/calculateElevation.ts",
"default": "./dist/calculateElevation.js"
}
}
"types"
:指向 TypeScript 文件,供开发时使用类型信息。"default"
:指向编译后的 JavaScript 文件(编译后会存放在 dist
目录中)。设置 "./calculateElevation": {
是因为 需要从该路径导入函数即
import {calculateElevation} from '@repo/elevation'
更新名称并保持一致性:
"name": "@repo/elevation"
配置完成后,elevation
包就可以被其他包引用了。只需在其他包中导入即可,例如:
import { calculateElevation } from "@repo/elevation";
在下一个视频中,我们将配置 TypeScript 设置,使我们的单一代码库能够处理 TypeScript 代码。
为了在项目中使用 TypeScript,需要配置一个 TypeScript 配置文件(tsconfig.json
)。幸运的是,我们已经在项目的 packages
文件夹根目录中有了一个 TypeScript 配置文件。在此基础上,我们可以在 elevation
项目中扩展配置。
tsconfig.json
文件在 elevation
文件夹的根目录中,创建 tsconfig.json
文件,并编写以下内容:
{
"extends": "@repo/typescript-config/base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
解释:
outDir
:编译后的文件输出目录,设置为 dist
。rootDir
:源文件目录,指定为 src
。src
。node_modules
和 dist
目录,以免影响编译速度。为了验证配置正确性,导航到 elevation
文件夹,并使用先前定义的 build
脚本运行 TypeScript 编译器:
pnpm build
该命令会运行 TypeScript 编译器,将 src
文件夹中的 TypeScript 文件转换为 JavaScript 文件,并输出到 dist
文件夹中。这样可以确保类型检查正常并且配置文件工作无误。
在成功编译后,我们可以将 elevation
包导入到其他项目中使用。接下来的视频中,我们将演示如何在另一个项目中导入这个包。
我们已创建了 elevation
包,并完成了配置,现在可以在项目中的其他地方引用这个包。在这个例子中,我们将在 Web 应用程序中使用它。
elevation
包在 web
应用的 page.tsx
文件中,我们将导入并使用 calculateElevation
函数:
import { calculateElevation } from "@repo/elevation/calculateElevation";
const elevation = calculateElevation(10000);
<h2>{elevation}</h2>
package.json
和 turbo.json
\snowtooth\apps\web\package.json
@repo/elevation
添加为依赖项。snowtooth\turbo.json
dist/**
到 outputs
,以便 Turbo Repo 在构建时正确引用 elevation
包的产物。"outputs": ["dist/**"]
在 web
文件夹中运行以下命令来安装 @repo/elevation
:
pnpm install
然后在项目根目录运行 Turbo 的构建命令:
turbo build
在 web
文件夹中运行开发服务器,查看应用:
pnpm run dev
打开浏览器访问 localhost:3000
,你应该能看到 10,000 英尺的转换结果,以米为单位显示在页面上。
elevation
。恭喜!我们的 Turbo 和 pnpm 设置都已成功完成,并且所有子项目和包的构建和管理都运行顺利。
当我们选择像 Turborepo 这样的 monorepo 工具时,通常会关注如何加快速度,因为 monorepo 往往包含大量文件和工具,每次构建可能需要较长时间。Turborepo 提供了一个强大的功能——缓存,这可以显著缩短构建时间。它可以在我们未更改文件的情况下避免重新编译。
查看当前缓存:在 snowtooth
目录中,运行以下命令:
pnpm run build
Turborepo 将会检测哪些任务是缓存的并加快执行速度。比如,如果上次构建的任务没有发生变化,那么它会直接从缓存中读取结果。
代码更改触发缓存失效:
elevation
函数进行修改,将其转换为将米数转换为英尺:
function calculateElevation(meters: number): number {
const feet = meters / 0.3048;
return Math.round(feet);
}
pnpm run build
由于对 elevation
函数进行了更改,Turborepo 会检测到缓存失效,并重新构建受影响的模块。
验证缓存功能:
pnpm run build
因为没有新的更改,此次所有构建任务都将从缓存中加载,大大缩短了构建时间。
在下一步中,您可以考虑配置远程缓存。通过远程缓存,您可以在多个开发环境中共享构建缓存,从而进一步提高开发效率。
Turborepo 的缓存机制使得在大型项目中管理和加快构建过程变得更加高效。
在前面的视频中,我们介绍了本地缓存如何加速构建过程,但如果您与团队合作,远程缓存会更高效。使用远程缓存,团队成员可以共享缓存结果,无需重复构建。
登录 Turbo:确保您在项目目录下,例如 snowtooth
目录,打开终端并输入以下命令登录:
turbo login
这将基于您的电子邮件地址进行授权。
链接到远程缓存:接下来,运行以下命令以启用远程缓存:
npx turbo link
系统会询问您是否希望启用远程缓存,并且您可以选择账号和团队名称,例如 Moon Highway
。成功之后,Turborepo CLI 将被授权使用该账户。
使用远程缓存构建:在项目中做出更改,比如在 elevation
函数中添加日志输出:
console.log(meters);
运行以下命令进行构建:
turbo build
第一次运行时,由于更改未被缓存,因此会出现缓存未命中的情况。再次运行构建命令时,由于使用了远程缓存,构建速度显著提升。
清除本地缓存:可以使用以下命令清除本地缓存:
rm -rf .turbo/cache
或者手动删除该目录。删除缓存后,运行 turbo build
,Turborepo 将从远程缓存中加载构建结果,大大提高速度。
远程缓存非常适合团队合作项目,使整个团队的构建速度得以提升。每次构建时,Turborepo 会从远程缓存中检索资源,减少重复计算。
Turborepo 提供了开发模式的配置,以便更高效地处理日常开发任务。以下是一些配置和命令,帮助我们在开发模式下优化项目的操作:
在 turbo.json
文件中,我们可以看到一个 dev
命令示例:
"cache": false
true
,表示这是一个持续运行的任务,会在我们进行交互时保持活跃。
"persistent": true
运行所有项目的开发任务:
turbo dev
该命令会在本地运行所有项目并持续监视文件更改。
监控单个项目:
通过过滤选项,我们可以只监控一个项目。例如,如果只想监控 docs
项目,可以使用以下命令:
turbo dev --filter docs
这样只会在 dev
模式下运行 docs
项目,这样更有助于集中精力处理单个项目的开发。
在日常开发中,可以通过这些命令有针对性地工作在不同的项目模块上,同时利用 Turborepo 的监视功能确保更改能够快速反映在项目上。这样既可以提高效率,也减少了无关模块的编译时间。
在 Turborepo 中处理环境变量时,特别是在使用 Next.js 项目时,以下是一些关键步骤和建议:
在 Next.js 应用中,通常使用 .env.local
文件来存储环境变量。你可以创建一个名为 MH_API_KEY
的变量,例如:
MH_API_KEY=your-real-api-key-here
通过在代码中引用这些环境变量,可以确保它们在运行时被正确加载。例如:
console.log(process.env.MH_API_KEY);
如果你对 .env.local
文件中的值进行了更改,Turborepo 会检测到缓存失效,因为这些变量被视为输入。当环境变量发生变化时,Turborepo 会重建项目。
你可以在 turbo.json
文件中指定环境变量文件作为输入:
{
"pipeline": {
"build": {
"inputs": ["**/*", ".env.local"]
}
}
}
你还可以根据需要添加其他文件,例如 .environment
文件。将它们列入 inputs
中,这样 Turborepo 就会自动检测这些文件中的更改并重新运行必要的任务。
可以在 turbo.json
中进一步配置多个环境变量文件,以确保它们在适当时生效。通过合理的配置,你可以确保不同的开发、测试和生产环境下使用不同的变量文件,从而提高应用的灵活性和可维护性。
在继续深入 Turborepo 使用的基础上,下一步可以开始考虑如何将它集成到你的持续集成(CI)管道中。不同的项目会有不同的需求,因此可以根据具体情况,使用以下一些 CI 工具:
此外,建议探索 Turbopack,这是 Turborepo 的一个补充工具,目前还在 beta 阶段,未来会更稳定。Turbopack 专注于 React Server 组件和 TypeScript 项目,可以显著加快构建速度,并优化缓存的效率。
通过在你的 CI 管道中使用 Turborepo 和 Turbopack,可以为开发团队带来更高效的体验,为企业级项目打下坚实的基础。
使用JavaScript和TypeScript构建可扩展的Monorepo项目 Building a Scalable Monorepo with JavaScript and TypeScript
01 - Introduction 01 - Scaling a codebase with a monorepo 02 - What you should know
02 - 1. Understanding Monorepos 01 - Working with a monorepo architecture 02 - Setting up the dev environment 03 - Creating a package 04 - Creating multiple packages 05 - Recursively running multiple packages
03 - 2. Configuring pnpm Workspaces 01 - Referencing local packages 02 - Challenge Sharing code across projects 03 - Solution Sharing code across projects 04 - Linking to local file paths
04 - 3. Exploring Turborepo 01 - Introducing Turborepo 02 - Installing external dependencies 03 - Creating an internal package 04 - Setting an internal dependency 05 - Writing a tsconfig file 06 - Running the build
05 - 4. Turborepo Integration 01 - Improving performance with build caching 02 - Utilizing remote caching 03 - Understanding developer mode features 04 - Handling environment variables
06 - Conclusion 01 - Next steps
01 - 引言
01 - 使用单一代码库(monorepo)扩展代码库
02 - 你需要了解的内容
02 - 1. 了解单一代码库(Monorepos)
01 - 使用单一代码库架构
02 - 设置开发环境
03 - 创建一个包
04 - 创建多个包
05 - 递归运行多个包
03 - 2. 配置 pnpm 工作区
01 - 引用本地包
02 - 挑战:在项目间共享代码
03 - 解决方案:在项目间共享代码
04 - 链接到本地文件路径
04 - 3. 探索 Turborepo
01 - 介绍 Turborepo
02 - 安装外部依赖项
03 - 创建内部包
04 - 设置内部依赖项
05 - 编写 tsconfig 文件
06 - 运行构建
05 - 4. Turborepo 集成
01 - 通过构建缓存提高性能
02 - 使用远程缓存
03 - 了解开发者模式功能
04 - 处理环境变量
06 - 总结
01 - 下一步