# 项目参考手册
项目引用是 TypeScript 3.0 中的一项新功能,它允许您将 TypeScript 程序构造成更小的部分。
通过这样做,您可以大大缩短构建时间,强制组件之间的逻辑分离,并以新的更好的方式组织您的代码。
我们还为 tsc
引入了一种新模式,即 --build
标志,它与项目引用协同工作,以实现更快的 TypeScript 构建。
# 一个示例项目
让我们看一个相当正常的程序,看看项目引用如何帮助我们更好地组织它。假设您有一个项目,其中包含两个模块 converter
和 units
,每个模块都有一个相应的测试文件:
/
├── 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
出现在输出文件夹名称中,就不可能同时构建test
和src
,这是您可能不想要的 - 仅更改实现文件中的内部结构需要再次对测试进行类型检查,即使这不会导致新的错误
- 仅更改测试需要再次对实现进行类型检查,即使没有任何更改
您可以使用多个 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 文件具有与它们配对的托管项目相同的隐式图形排序。如果您的解决方案是这样的,您可以继续使用 msbuild
和 tsc -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.json
、src/tsconfig.json
和 src/tsc/tsconfig.json
作为关键示例。
# 相关模块的结构
一般来说,使用相关模块转换 repo 不需要太多。只需在给定父文件夹的每个子目录中放置一个 tsconfig.json
文件,并将 reference
s 添加到这些配置文件中以匹配程序的预期分层。您需要将 outDir
设置为输出文件夹的显式子文件夹,或将 rootDir
设置为所有项目文件夹的公共根目录。
# outFiles 的结构
使用 outFile
进行编译的布局更加灵活,因为相对路径并不重要。要记住的一件事是,您通常不希望在 "last" 项目之前使用 prepend
- 这将缩短构建时间并减少任何给定构建所需的 I/O 数量。TypeScript repo 本身就是一个很好的参考——我们有一些 "library" 项目和一些 "endpoint" 项目; "endpoint" 项目尽可能小,并且只引入他们需要的库。
← tsc CLI 选项 与构建工具集成 →