# 项目参考手册

项目引用是 TypeScript 3.0 中的一项新功能,它允许您将 TypeScript 程序构造成更小的部分。

通过这样做,您可以大大缩短构建时间,强制组件之间的逻辑分离,并以新的更好的方式组织您的代码。

我们还为 tsc引入了一种新模式,即 --build标志,它与项目引用协同工作,以实现更快的 TypeScript 构建。

# 一个示例项目

让我们看一个相当正常的程序,看看项目引用如何帮助我们更好地组织它。假设您有一个项目,其中包含两个模块 converterunits,每个模块都有一个相应的测试文件:


/
├── src/
│   ├── converter.ts
│   └── units.ts
├── test/
│   ├── converter-tests.ts
│   └── units-tests.ts
└── tsconfig.json

测试文件导入实现文件并做一些测试:

// converter-tests.ts
import * as converter from "../converter";

assert.areEqual(converter.celsiusToFahrenheit(0), 32);

以前,如果您使用单个 tsconfig 文件,则使用此结构相当尴尬:

  • 实现文件可以导入测试文件
  • 如果没有 src出现在输出文件夹名称中,就不可能同时构建 testsrc,这是您可能不想要的
  • 仅更改实现文件中的内部结构需要再次对测试进行类型检查,即使这不会导致新的错误
  • 仅更改测试需要再次对实现进行类型检查,即使没有任何更改

您可以使用多个 tsconfig 文件来解决其中一些问题,但会出现新问题:

  • 没有内置的最新检查,所以你最终总是运行 tsc两次
  • 调用 tsc两次会导致更多的启动时间开销
  • tsc -w不能同时在多个配置文件上运行

项目参考可以解决所有这些问题以及更多问题。

# 什么是项目参考手册?

tsconfig.json文件有一个新的顶级属性 references 。它是一个对象数组,指定要引用的项目:


{
    "compilerOptions": {
        // The usual
    },
    "references": [
        { "path": "../src" }
    ]
}

每个引用的 path属性可以指向包含 tsconfig.json文件的目录,或者指向配置文件本身(可以有任何名称)。

当您引用一个项目时,会发生新的事情:

  • 从引用的项目导入模块将加载其输出声明文件 (.d.ts)
  • 如果引用的项目产生 outFile ,则输出文件 .d.ts文件的声明将在此项目中可见
  • 如果需要,构建模式(见下文)将自动构建引用的项目

通过分成多个项目,您可以大大提高类型检查和编译的速度,减少使用编辑器时的内存使用量,并改进程序逻辑分组的执行。

# composite

引用的项目必须启用新的 composite 设置。需要此设置以确保 TypeScript 可以快速确定在哪里可以找到引用项目的输出。启用 composite 标志会改变一些事情:

  • rootDir 设置,如果没有显式设置,默认为包含 tsconfig文件的目录
  • 所有实现文件必须与 include 模式匹配或列在 files 数组中。如果违反此约束,tsc将通知您未指定哪些文件
  • declaration 必须开启

# declarationMap

我们还添加了对 声明源映射 的支持。如果启用 declarationMap ,您将能够使用 "Go to Definition" 和 Rename 等编辑器功能在支持的编辑器中跨项目边界透明地导航和编辑代码。

# prepend 与 outFile

您还可以使用引用中的 prepend选项启用在依赖项的输出之前添加:

   "references": [
       { "path": "../utils", "prepend": true }
   ]

前置项目将在当前项目的输出之上包含项目的输出。所有输出文件(.js.d.ts.js.map.d.ts.map)都将正确发出。

tsc只会使用磁盘上的现有文件来执行此过程,因此可以创建一个无法生成正确输出文件的项目,因为某些项目的输出将在结果文件中出现多次。例如:


   A
  ^ ^
 /   \
B     C
 ^   ^
  \ /
   D

在这种情况下,重要的是不要在每个引用前添加,因为您最终会在 D的输出中得到 A的两个副本 - 这可能会导致意外结果。

# 项目参考手册的注意事项

项目引用有一些您应该注意的权衡取舍。

由于依赖项目使用从其依赖项构建的 .d.ts文件,因此您必须检查某些构建输出或在克隆项目后构建项目,然后才能在编辑器中导航项目而不会看到虚假错误。

使用 VS Code(自 TS 3.7 起)时,我们有一个幕后的内存 .d.ts生成过程,应该能够缓解这种情况,但它有一些性能影响。对于非常大的复合项目,您可能希望使用 disableSourceOfProjectReferenceRedirect 选项 禁用此功能。

此外,为了保持与现有构建工作流的兼容性,除非使用 --build开关调用,否则 tsc不会自动构建依赖项。让我们进一步了解--build

# TypeScript 的构建模式

期待已久的功能是 TypeScript 项目的智能增量构建。在 3.0 中,您可以将 --build标志与 tsc一起使用。这实际上是 tsc的一个新入口点,它的行为更像是一个构建协调器,而不是一个简单的编译器。

运行 tsc --build(简称 tsc -b)将执行以下操作:

  • 查找所有引用的项目
  • 检测它们是否是最新的
  • 以正确的顺序构建过时的项目

您可以为 tsc -b提供多个配置文件路径(例如 tsc -b src test)。与 tsc -p一样,如果配置文件名为 tsconfig.json,则无需指定配置文件名本身。

# tsc -b 命令行

您可以指定任意数量的配置文件:


 > tsc -b                            # Use the tsconfig.json in the current directory
 > tsc -b src                        # Use src/tsconfig.json
 > tsc -b foo/prd.tsconfig.json bar  # Use foo/prd.tsconfig.json and bar/tsconfig.json

不必担心在命令行上对您传递的文件进行排序 - 如果需要,tsc会重新排序它们,以便始终首先构建依赖项。

还有一些特定于 tsc -b的标志:

  • --verbose :打印出详细日志以解释发生了什么(可以与任何其他标志结合使用)
  • --dry:显示将要做什么,但实际上并没有构建任何东西
  • --clean:删除指定项目的输出(可与--dry组合)
  • --force :就好像所有项目都过时一样
  • --watch:观看模式(不得与除 --verbose 之外的任何标志组合)

# 警告

通常,tsc将在存在语法或类型错误时产生输出(.js.d.ts),除非 noEmitOnError 已打开。在增量构建系统中这样做会非常糟糕 - 如果您的一个过时依赖项出现新错误,您只会看到一次,因为后续构建将跳过构建现在最新的项目。出于这个原因,tsc -b的作用就像为所有项目启用了 noEmitOnError

如果您签入任何构建输出(.js.d.ts.d.ts.map等),您可能需要在某些源代码控制操作之后运行 --force 构建,具体取决于您的源代码控制工具是否保留本地副本和远程副本之间的时间戳。

# MSBuild

如果你有一个 msbuild 项目,你可以通过添加启用构建模式


    <TypeScriptBuildMode>true</TypeScriptBuildMode>

到您的项目文件。这将启用自动增量构建和清理。

请注意,与 tsconfig.json/ -p一样,现有的 TypeScript 项目属性将不被尊重 - 所有设置都应使用您的 tsconfig 文件进行管理。

一些团队已经建立了基于 msbuild 的工作流,其中 tsconfig 文件具有与它们配对的托管项目相同的隐式图形排序。如果您的解决方案是这样的,您可以继续使用 msbuildtsc -p以及项目引用;这些是完全可互操作的。

# Guidance

# 整体结构

对于更多 tsconfig.json文件,您通常会希望使用 配置文件继承 来集中您的常用编译器选项。这样,您可以更改一个文件中的设置,而不必编辑多个文件。

另一个好的做法是有一个 "solution" tsconfig.json文件,它只是将 references 用于所有叶节点项目,并将 files 设置为空数组(否则解决方案文件将导致文件的双重编译)。请注意,从 3.0 开始,如果 tsconfig.json文件中至少有一个 reference,则有一个空的 files 数组不再是错误。

这提供了一个简单的入口点;例如在 TypeScript 存储库中,我们只需运行 tsc -b src来构建所有端点,因为我们列出了 src/tsconfig.json中的所有子项目

您可以在 TypeScript 存储库中看到这些模式 - 请参阅 src/tsconfig_base.jsonsrc/tsconfig.jsonsrc/tsc/tsconfig.json作为关键示例。

# 相关模块的结构

一般来说,使用相关模块转换 repo 不需要太多。只需在给定父文件夹的每个子目录中放置一个 tsconfig.json文件,并将 references 添加到这些配置文件中以匹配程序的预期分层。您需要将 outDir 设置为输出文件夹的显式子文件夹,或将 rootDir 设置为所有项目文件夹的公共根目录。

# outFiles 的结构

使用 outFile 进行编译的布局更加灵活,因为相对路径并不重要。要记住的一件事是,您通常不希望在 "last" 项目之前使用 prepend- 这将缩短构建时间并减少任何给定构建所需的 I/O 数量。TypeScript repo 本身就是一个很好的参考——我们有一些 "library" 项目和一些 "endpoint" 项目; "endpoint" 项目尽可能小,并且只引入他们需要的库。

Last Updated: 5/25/2023, 2:35:11 PM