各位靓仔靓女,晚上好!我是你们的老朋友,今天咱们来聊聊 TypeScript 项目的“内功心法”—— tsconfig.json
配置优化。这玩意儿看似简单,实则玄机重重,配置得好,编译速度嗖嗖的,项目结构井井有条;配置不好,编译慢如蜗牛,代码一团乱麻。
今天咱们就深入浅出,把 tsconfig.json
扒个底朝天,让你的 TypeScript 项目起飞!准备好了吗?Let’s go!
1. 啥是 tsconfig.json
?
简单来说,tsconfig.json
就是 TypeScript 编译器的配置文件。它告诉编译器:
- 哪些文件需要编译?
- 用什么方式编译?
- 编译后生成什么?
你可以把它想象成一个菜谱,告诉厨师(编译器)用哪些食材(TypeScript 文件),怎么烹饪(编译选项),最后做出什么菜(JavaScript 文件)。
2. tsconfig.json
的基本结构
一个最简单的 tsconfig.json
可能长这样:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"outDir": "./dist",
"sourceMap": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules"
]
}
让我们逐一解读:
compilerOptions
: 这是核心!所有的编译选项都在这里面。target
: 指定编译后的 JavaScript 版本。es5
表示编译成 ES5 版本的 JavaScript,保证兼容性。module
: 指定模块化规范。commonjs
是 Node.js 常用的模块化规范。outDir
: 指定编译后 JavaScript 文件的输出目录。./dist
表示输出到项目根目录下的dist
文件夹。sourceMap
: 是否生成 source map 文件。true
表示生成,方便调试。
include
: 指定需要包含的 TypeScript 文件。"src/**/*"
表示包含src
目录下所有文件和子目录下的所有文件。exclude
: 指定需要排除的 TypeScript 文件或目录。"node_modules"
表示排除node_modules
目录,因为我们不需要编译 node_modules 里面的东西。
3. 优化编译速度的秘密武器
编译速度慢,是很多 TypeScript 项目的痛点。但别慌,我们可以通过优化 tsconfig.json
来显著提升编译速度。
3.1. 减少文件搜索范围:include
和 exclude
include
和 exclude
就像是过滤网,决定了哪些文件会被编译器扫描。如果配置不当,编译器会扫描大量不必要的文件,导致编译速度变慢。
- 原则: 尽可能精确地指定需要编译的文件,排除不需要编译的文件。
反例:
{
"compilerOptions": {
// ...
},
"include": [
"**/*" // 包含所有文件,包括 node_modules!
]
}
这种写法会导致编译器扫描整个项目,包括 node_modules
目录,速度肯定慢。
正例:
{
"compilerOptions": {
// ...
},
"include": [
"src/**/*" // 只包含 src 目录下的 TypeScript 文件
],
"exclude": [
"node_modules",
"dist",
"**/*.spec.ts" // 排除测试文件
]
}
只包含 src
目录下的 TypeScript 文件,并排除 node_modules
、dist
和测试文件,可以大大减少编译器的工作量。
更精细的控制:files
如果你的项目结构比较简单,或者你只想编译特定的几个文件,可以使用 files
选项:
{
"compilerOptions": {
// ...
},
"files": [
"src/index.ts",
"src/utils.ts"
]
}
files
选项直接指定要编译的文件列表,编译器不会进行任何文件搜索,速度最快。但是,使用 files
需要手动维护文件列表,不太灵活。
3.2. 开启增量编译:incremental
和 tsBuildInfoFile
增量编译是指编译器只编译发生改变的文件,而不是每次都编译整个项目。这可以显著提升编译速度,尤其是在大型项目中。
incremental
: 开启增量编译。tsBuildInfoFile
: 指定增量编译的缓存文件。
{
"compilerOptions": {
// ...
"incremental": true,
"tsBuildInfoFile": "./dist/.tsbuildinfo"
}
}
开启 incremental
后,编译器会将上次编译的信息保存在 .tsbuildinfo
文件中。下次编译时,编译器会读取这个文件,只编译发生改变的文件。
3.3. 跳过类型检查:skipLibCheck
TypeScript 的类型检查非常强大,但也比较耗时。如果你确定你的第三方库没有类型错误,可以跳过对它们的类型检查,提升编译速度。
skipLibCheck
: 跳过对声明文件(.d.ts
)的类型检查。
{
"compilerOptions": {
// ...
"skipLibCheck": true
}
}
注意: 开启 skipLibCheck
后,如果你的第三方库真的有类型错误,TypeScript 编译器可能不会报错。因此,只有在你确定你的第三方库没有问题的情况下,才建议开启这个选项。
3.4. 使用更快的模块解析策略:moduleResolution
moduleResolution
指定 TypeScript 编译器如何查找模块。不同的模块解析策略,速度差异很大。
moduleResolution
: 指定模块解析策略。
常见的模块解析策略有两种:
node
: Node.js 风格的模块解析。classic
: TypeScript 早期使用的模块解析。
一般来说,node
策略比 classic
策略更快,更推荐使用。
{
"compilerOptions": {
// ...
"moduleResolution": "node"
}
}
3.5. 使用 esbuild
或 swc
替代 tsc
tsc
是 TypeScript 官方的编译器,功能强大,但速度相对较慢。esbuild
和 swc
是用 Go 和 Rust 编写的,速度非常快,可以作为 tsc
的替代品。
esbuild
: 一个用 Go 编写的极速 JavaScript bundler 和编译器。swc
: 一个用 Rust 编写的极速 TypeScript / JavaScript 工具链。
要使用 esbuild
或 swc
,你需要安装相应的工具,并配置你的构建流程。这里不详细介绍,你可以参考它们的官方文档。
4. 优化项目结构的利器
tsconfig.json
不仅仅可以优化编译速度,还可以帮助你更好地管理项目结构。
4.1. 使用 baseUrl
和 paths
简化模块导入
在大型项目中,模块导入路径可能会很长,例如:
import { Utils } from "../../../utils/utils";
这不仅难以阅读,而且在移动文件时需要修改大量的导入路径。我们可以使用 baseUrl
和 paths
来简化模块导入。
baseUrl
: 指定模块解析的基准路径。paths
: 指定模块路径的映射。
{
"compilerOptions": {
// ...
"baseUrl": "./src",
"paths": {
"@utils/*": ["utils/*"],
"@components/*": ["components/*"]
}
}
}
配置了 baseUrl
和 paths
后,我们可以这样导入模块:
import { Utils } from "@utils/utils";
import { Button } from "@components/button";
这不仅更加简洁,而且在移动文件时,只需要修改 tsconfig.json
中的 paths
配置即可。
4.2. 使用 references
构建多项目
在大型项目中,我们可能会将项目拆分成多个子项目,每个子项目都有自己的 tsconfig.json
文件。references
可以让我们将这些子项目组织起来,构建一个完整的项目。
references
: 指定引用的其他tsconfig.json
文件。
例如,我们有两个子项目:frontend
和 backend
。它们的 tsconfig.json
文件分别如下:
frontend/tsconfig.json
:
{
"compilerOptions": {
"composite": true, // 必须开启 composite
"declaration": true, // 必须开启 declaration
// ...
},
"include": ["src/**/*"],
"references": []
}
backend/tsconfig.json
:
{
"compilerOptions": {
"composite": true, // 必须开启 composite
"declaration": true, // 必须开启 declaration
// ...
},
"include": ["src/**/*"],
"references": []
}
现在,我们可以创建一个顶层的 tsconfig.json
文件,引用这两个子项目:
tsconfig.json
:
{
"files": [],
"references": [
{ "path": "./frontend" },
{ "path": "./backend" }
],
"compilerOptions": {
"composite": true,
"declaration": true,
"declarationMap": true,
"incremental": true,
"skipLibCheck": true,
"moduleResolution": "node",
"module": "esnext",
"target": "esnext",
"lib": ["es2021", "dom"],
"strict": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"types": ["node"]
}
}
注意 composite
和 declaration
必须为 true。
这样,当我们编译顶层的 tsconfig.json
文件时,TypeScript 编译器会自动编译 frontend
和 backend
两个子项目。
5. 常用 compilerOptions
选项汇总
为了方便大家查阅,我把常用的 compilerOptions
选项整理成一个表格:
选项 | 描述 | 建议值 |
---|---|---|
target |
指定编译后的 JavaScript 版本。 | es5 (兼容性好), esnext (最新特性) |
module |
指定模块化规范。 | commonjs (Node.js), esnext (现代浏览器) |
outDir |
指定编译后 JavaScript 文件的输出目录。 | ./dist |
sourceMap |
是否生成 source map 文件。 | true (方便调试) |
incremental |
开启增量编译。 | true |
tsBuildInfoFile |
指定增量编译的缓存文件。 | ./dist/.tsbuildinfo |
skipLibCheck |
跳过对声明文件(.d.ts )的类型检查。 |
true (如果确定第三方库没有类型错误) |
moduleResolution |
指定模块解析策略。 | node |
baseUrl |
指定模块解析的基准路径。 | ./src |
paths |
指定模块路径的映射。 | {"@utils/*": ["utils/*"]} |
strict |
开启所有严格类型检查选项。 | true (推荐) |
esModuleInterop |
允许 CommonJS 模块和 ES 模块互相导入。 | true (推荐) |
resolveJsonModule |
允许导入 JSON 文件。 | true |
isolatedModules |
确保每个文件都可以被独立编译(与 Babel 兼容)。 | true (推荐) |
noEmit |
是否只进行类型检查,不生成 JavaScript 文件。 | true (在构建流程中使用其他工具生成 JavaScript 文件时) |
jsx |
指定 JSX 语法的处理方式。 | react (使用 React 的 JSX 转换), react-jsx (使用 React 17+ 的 JSX 转换) |
declaration |
是否生成声明文件(.d.ts )。 |
true (如果你的项目是一个库) |
declarationMap |
是否生成声明文件的 source map 文件。 | true (方便调试) |
composite |
开启 composite 模式,用于构建多项目。 | true (如果你的项目是一个多项目) |
noImplicitAny |
在表达式和声明上有隐含的 any 类型时报错。 |
true (推荐,避免 any 类型的滥用) |
noUnusedLocals |
报告未使用的局部变量的错误。 | true (推荐,避免代码冗余) |
noUnusedParameters |
报告未使用的函数参数的错误。 | true (推荐,避免代码冗余) |
removeComments |
移除所有的注释。 | true (在生产环境中移除注释可以减小文件体积) |
preserveConstEnums |
不要移除 const enum 声明。为了兼容性,不建议使用 const enum,建议使用 union type 代替。 | false (移除 const enum) |
useDefineForClassFields |
是否使用 define 方式来定义 class field,默认是 false ,使用赋值的方式。 在 esnext 下建议开启,更符合规范。 |
true (在 esnext 下建议开启) |
6. 最佳实践总结
- 精确指定
include
和exclude
,减少文件搜索范围。 - 开启增量编译,提升编译速度。
- 如果确定第三方库没有类型错误,可以跳过对它们的类型检查。
- 使用
node
模块解析策略。 - 使用
baseUrl
和paths
简化模块导入。 - 使用
references
构建多项目。 - 开启
strict
模式,保证代码质量。 - 定期检查
tsconfig.json
,根据项目需求进行调整。
7. 结语
tsconfig.json
的配置是一个持续学习和优化的过程。希望今天的分享能帮助你更好地理解和使用 tsconfig.json
,提升你的 TypeScript 项目的编译速度和项目结构管理能力。记住,没有最好的配置,只有最适合你的配置!
下次再见,各位!