工程引用是 TypeScript 3.0 的新特性, 它支持将 TypeScript 程序的结构分割成更小的组成部分. 这样能够改善构建时间 (打开 composite 会自动开启增量编译), 强制在逻辑上对组件进行分离, 更好地组织你的代码.node
考察下面的代码结构: 假设这是一个先后端未分离的项目, client 目录下存放的是客户端代码; server 目录下存放的服务端代码; common 存放的是一些共用代码, 好比一些 util 方法, client/index.ts
和 server/index.ts
会引用这里的代码.git
__test__ 目录则存放的是一些单元测试的代码, 它会分别引用 src/client/index.ts
和 src/server/index.ts
.github
而且整个工程只有一个 tsconfig.json
, 它主要的配置是 "outDir": "./dist"
, 也就是将编译后的文件存放到根目录的 dist 文件夹.json
.
├── src
│ ├── client
│ │ ├── index.ts
│ ├── common
│ │ ├── index.ts
│ ├── server
│ │ ├── index.ts
├── __test__
│ ├── client.spec.ts
│ ├── server.spec.ts
├── package.json
├── tsconfig.json
└── yarn.lock
├── README.md
复制代码
经过 tsc
命令进行编译, dist 文件下大体以下.后端
.
├── dist
│ ├── src
│ │ ├── client
│ │ ├── common
│ │ ├── server
│ ├── __test__
复制代码
经过上面的 code structure, 能够罗列出几个痛点:session
咱们但愿 src 下面的文件直接被编译到 dist 目录下, 但因为 __test__ 的存在而达不到这样的效果.单元测试
咱们没法单独构建 client 端, 或者 server 端的代码.测试
咱们不但愿把 __test__ 构建到 dist 目录下.ui
经过上面的代码, 咱们能感觉到仅仅一个 tsconfig.json 文件没法灵活的 hold 住个性化的编译配置, 所以尝试给每一个目录都增长一个 tsconfig.json. (为了便于区分每一个 tsconfig.json 文件, 下面将使用 ①②③④⑤ 序号代替)es5
.
├── src
│ ├── client
│ │ ├── index.ts
│ │ ├── tsconfig.json // ①
│ ├── common
│ │ ├── index.ts
│ │ ├── tsconfig.json // ②
│ ├── server
│ │ ├── index.ts
│ │ ├── tsconfig.json // ③
├── __test__
│ ├── client.spec.ts
│ ├── server.spec.ts
│ ├── tsconfig.json // ④
├── package.json
├── tsconfig.json // ⑤
└── yarn.lock
├── README.md
复制代码
接下来改造一下根 tsconfig.json 文件, 也就是 ⑤.
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
// "outDir": "./dist" 关闭 outDir 选项, 即不在根配置中指定输入目录
"composite": true, // 使用 composite, 它意味着工程能够被引用, 并支持增量编译
"declaration": true // 使用 composite 选项必须开启 declaration
}
}
复制代码
接下来改造 ①, 由于 ① 和 ③ 的配置大体相同, 这里再也不赘述.
{
"extends": "../../tsconfig.json", // 首先导入 ⑤
"compilerOptions": {
"outDir": "../../dist/client" // 指定输入目录
},
"references": [{ "path": "../common" }] // 由于 client 引用了 common, 故须要将 common 引入进来
}
复制代码
对于 ②, 由于它没有引用其余的模块, 所以只须要配置好 outDir 便可, 代码以下:
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/common"
}
}
复制代码
对于 ④, 为了避免让测试文件被编译到 dist 目录下, 就让编译后的文件也存放到 __test__ 好了.
{
"extends": "../tsconfig.json",
"references": [{ "path": "../src/client" }, { "path": "../src/server" }]
}
复制代码
既然配置写好了, 下面就能够愉快的编译了. 为了支持工程引用, TypeScript 使用了 build
的构建模式, 它能够构建单独的工程, 相关依赖也能够被自动构建, 下面咱们构建 server 端的应用, 其中 -b 是 build 的简写, verbose
能够打印出一些构建信息.
tsc -b src/server --verbose
复制代码
当 server 端被构建完毕后, server 文件夹和它所依赖的 common 文件夹就会被编译到 dist 目录下, 所以当你再编译 client 时, common 就不用被重复编译, 这样就提高了编译效率. 此外, 它会在每一个目录下生成增量编译文件, 这样下次编译时就很快了.
此外, 你能够经过 --clean
参数来清除某个工程已构建的文件, 下面是清理 __test__ 产生的文件.
tsc -b __test__ --clean
复制代码
工程引用的优势以下:
解决了输出目录的结构问题
解决了单个工程的构建问题
经过增量编译提升了编译效率
其实 TypeScript 的官方源码已经使用了工程引用技术, 在 src 目录下有一个根 tsconfig-base.json, 在其余的目录下, 如server目录下又有本身的 tsconfig.json, 摘录以下:
{
"extends": "../tsconfig-base",
"compilerOptions": {
"removeComments": false,
"outFile": "../../built/local/server.js",
"preserveConstEnums": true,
"types": ["node"]
},
"references": [
{ "path": "../compiler" },
{ "path": "../jsTyping" },
{ "path": "../services" }
],
"files": [
"types.ts",
"utilities.ts",
"protocol.ts",
"scriptInfo.ts",
"typingsCache.ts",
"project.ts",
"editorServices.ts",
"packageJsonCache.ts",
"session.ts",
"scriptVersionCache.ts"
]
}
复制代码