工程引用
随着工程规模的扩大,一个工程中的代码量可能会达到数十万行的级别。当 TypeScript 编译器对数十万行代码进行类型检查时可能会遇到性能问题。分而治之 是解决该问题的一种策略,我们可以将一个较大的工程拆分为独立的子工程,然后将多个子工程关联在一起。
工程引用是 TypeScript 3.0 引入的新功能。它允许一个 TypeScript 工程引用一个或多个其他的 TypeScript 工程。借助于工程引用,我们可以将一个原本较大的 TypeScript 工程拆分成多个 TypeScript 工程,并设置它们之间的依赖关系。每个 TypeScript 工程都可以进行独立的配置与类型检查。当我们修改其中一个工程的代码时,会按需对其他工程进行类型检查,因此能够显著地提高类型检查的效率。同时,使用工程引用还能够更好地组织代码结构,从逻辑上将软件拆分为可以重用的组件,将实现细节封装在组件内部,并通过定义好的接口与外界交互。
本节将通过一个具体示例来介绍如何使用 tsconfig.json
配置文件来配置不同 TypeScript 工程之间的引用。在本节的最后会介绍一种 solution
模式,它能够帮助我们很好地管理包含多个 TypeScript 工程的项目。
使用工程引用
若一个目录中包含 tsconfig.json
配置文件,那么该目录将被视为 TypeScript 工程的根目录。在使用工程引用时,需要在 tsconfig.json
配置文件中进行以下配置:
-
使用
references
属性配置当前工程所引用的其他工程。 -
被引用的工程必须启用
composite
编译选项。
references
tsconfig.json
配置文件有一个顶层属性 references
。它的值是对象数组,用于设置引用的工程。示例如下:
{
"references": [
{ "path": "../pkg1" },
{ "path": "../pkg2/tsconfig.release.json" },
]
}
其中,path
的值既可以是指向含有 tsconfig.json
配置文件的目录,也可以直接指向某一个配置文件,此时配置文件名可以不为 tsconfig.json
。此例中的工程引用了两个工程。
工程引用示例
该示例项目由 C:\app\src
和 C:\app\test
两个工程组成,其目录结构如下:
C:\app
|-- src
| |-- index.ts
| `-- tsconfig.json
`-- test
|-- index.spec.ts
`-- tsconfig.json
index.ts
文件的内容如下:
export function add(x: number, y: number) {
return x + y;
}
index.spec.ts
文件的内容如下:
import { add } from '../src/index';
if (add(1, 2) === 3) {
console.log('pass');
} else {
console.log('failed');
}
--build
TypeScript 提供了一种新的构建模式来配合工程引用的使用,它就是 --build
模式(简写为 -b
)。在该模式下,编译器能够进行增量构建。具体的使用方式如下所示:
tsc --build
当使用该命令构建 TypeScript 工程时,编译器会执行如下操作:
-
查找当前工程所引用的工程。
-
检查当前工程和引用的工程是否有更新。
-
若工程有更新,则根据依赖顺序重新构建它们;若没有更新,则不进行重新构建。
下面还是以上一节中的示例项目为例,目录结构如下:
C:\app
|-- src
| |-- index.ts
| `-- tsconfig.json
`-- test
|-- index.spec.ts
`-- tsconfig.json
在 C:\app
目录下使用 --build
模式首次构建 C:\app\test
工程。示例如下:
tsc --build test --verbose
tsc
命令的运行结果如下:
[6:00:00 PM] Projects in this build:
* src/tsconfig.json
* test/tsconfig.json
[6:00:00 PM] Project 'src/tsconfig.json' is out of date because
output file 'src/index.js' does not exist
[6:00:00 PM] Building project 'src/tsconfig.json'...
[6:00:00 PM] Project 'test/tsconfig.json' is out of date because
output file 'test/index.spec.js' does not exist
[6:00:00 PM] Building project 'test/tsconfig.json'...
通过以上运行结果能够看到,当构建 C:\app\test 工程时,编译器先确定参与本次构建的所有工程,然后根据引用顺序对待编译的工程进行排序。此例中的顺序为先编译 C:\app\src 工程,后编译 C:\app\test 工程。接下来开始尝试编译 C:\app\src 工程。由于之前没有编译过该工程,它不处于最新状态,因此编译器会编译 C:\app\src 工程。同理,在编译完 C:\app\src 工程后会接着编译 C:\app\test 工程。
假设自上一次编译完 C:\app\test 工程之后,没有对 C:\app\src 工程和 C:\app\test 工程做任何修改。当再一次使用 --build 模式构建 C:\app\test 工程时,运行结果如下:
[6:00:01 PM] Projects in this build:
* src/tsconfig.json
* test/tsconfig.json
[6:00:01 PM] Project 'src/tsconfig.json' is up to date because
newest input 'src/index.ts' is older than oldest
output 'src/index.js'
[6:00:01 PM] Project 'test/tsconfig.json' is up to date because
newest input 'test/index.spec.ts' is older than
oldest output 'test/index.spec.js'
通过以上运行结果能够看到,编译器能够检测出 C:\app\src 工程和 C:\app\test 工程没有发生变化,因此也就没有重新编译这两个工程。
C:\app\src 工程和 C:\app\test 工程编译后的目录结构如下:
C:\app
|-- src
| |-- index.d.ts
| |-- index.js
| |-- index.ts
| |-- tsconfig.tsbuildinfo
| `-- tsconfig.json
`-- test
|-- index.spec.ts
|-- index.spec.js
`-- tsconfig.json
在 C:\app\src 工程和 C:\app\test 工程中都编译生成了对应的 .js
文件。
在 C:\app\src 工程中,由于启用了 --composite
编译选项,它会自动启用 --declaration
编译选项,因此编译后生成了 index.d.ts
声明文件。
在 C:\app\src 工程中还生成了 tsconfig.tsbuildinfo
文件,它保存了该工程本次构建的详细信息,编译器正是通过查看该文件来判断当前工程是否需要重新编译。
TypeScript 还提供了以下一些仅适用于 --build
模式的编译选项:
-
--verbose,打印构建的详细日志信息,可以与以下编译选项一起使用。
-
--dry,打印将执行的操作,而不进行真正的构建。
-
--clean,删除工程中编译输出的文件,可以与 --dry 一起使用。
-
--force,强制重新编译工程,而不管工程是否有更新。
-
--watch,观察模式,执行编译命令后不退出,等待工程有更新后自动地再次编译工程。
solution模式
当一个项目由多个工程组成时,我们可以新创建一个 solution
工程来管理这些工程。solution
工程本身不包含任何实际代码,它只是引用了项目中所有的工程并将它们关联在一起。在构建项目时,只需要构建 solution
工程就能够构建整个项目。
接下来,我们将之前的 C:\app\src 工程和 C:\app\test 工程重构为 solution
模式。修改后的目录结构如下:
C:\app
|-- src
| |-- index.ts
| `-- tsconfig.json
|-- test
| |-- index.spec.ts
| `-- tsconfig.json
`-- tsconfig.json
我们在 C:\app 目录下新建了一个 tsconfig.json
配置文件,目的是让 C:\app 成为一个 solution
工程。tsconfig.json
配置文件的内容如下:
{
"files": [],
"include": [],
"references": [
{ "path": "src" },
{ "path": "test" }
]
}
在该配置文件中同时将 C:\app\src 工程和 C:\app\test 工程设置为引用工程。此外,必须将 files
和 include
属性设置为空数组,否则编译器将会重复编译 C:\app\src 工程和 C:\app\test 工程。
在 C:\app 目录下使用 --build
模式来编译该工程,如下所示:
tsc --build --verbose
tsc
命令的运行结果如下:
[6:00:10 PM] Projects in this build:
* src/tsconfig.json
* test/tsconfig.json
* tsconfig.json
[6:00:10 PM] Project 'src/tsconfig.json' is out of date because
output file 'src/index.js' does not exist
[6:00:10 PM] Building project 'src/tsconfig.json'...
[6:00:10 PM] Project 'test/tsconfig.json' is out of date because
output file 'test/index.spec.js' does not exist
[6:00:10 PM] Building project 'test/tsconfig.json'...
通过以上运行结果能够看到,编译器能够正确地编译 C:\app\src
工程和 C:\app\test
工程。