JS `tsconfig.json` 配置优化:提升编译速度与项目结构管理

各位靓仔靓女,晚上好!我是你们的老朋友,今天咱们来聊聊 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. 减少文件搜索范围:includeexclude

includeexclude 就像是过滤网,决定了哪些文件会被编译器扫描。如果配置不当,编译器会扫描大量不必要的文件,导致编译速度变慢。

  • 原则: 尽可能精确地指定需要编译的文件,排除不需要编译的文件。

反例:

{
  "compilerOptions": {
    // ...
  },
  "include": [
    "**/*" // 包含所有文件,包括 node_modules!
  ]
}

这种写法会导致编译器扫描整个项目,包括 node_modules 目录,速度肯定慢。

正例:

{
  "compilerOptions": {
    // ...
  },
  "include": [
    "src/**/*" // 只包含 src 目录下的 TypeScript 文件
  ],
  "exclude": [
    "node_modules",
    "dist",
    "**/*.spec.ts" // 排除测试文件
  ]
}

只包含 src 目录下的 TypeScript 文件,并排除 node_modulesdist 和测试文件,可以大大减少编译器的工作量。

更精细的控制:files

如果你的项目结构比较简单,或者你只想编译特定的几个文件,可以使用 files 选项:

{
  "compilerOptions": {
    // ...
  },
  "files": [
    "src/index.ts",
    "src/utils.ts"
  ]
}

files 选项直接指定要编译的文件列表,编译器不会进行任何文件搜索,速度最快。但是,使用 files 需要手动维护文件列表,不太灵活。

3.2. 开启增量编译:incrementaltsBuildInfoFile

增量编译是指编译器只编译发生改变的文件,而不是每次都编译整个项目。这可以显著提升编译速度,尤其是在大型项目中。

  • 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. 使用 esbuildswc 替代 tsc

tsc 是 TypeScript 官方的编译器,功能强大,但速度相对较慢。esbuildswc 是用 Go 和 Rust 编写的,速度非常快,可以作为 tsc 的替代品。

  • esbuild: 一个用 Go 编写的极速 JavaScript bundler 和编译器。
  • swc: 一个用 Rust 编写的极速 TypeScript / JavaScript 工具链。

要使用 esbuildswc,你需要安装相应的工具,并配置你的构建流程。这里不详细介绍,你可以参考它们的官方文档。

4. 优化项目结构的利器

tsconfig.json 不仅仅可以优化编译速度,还可以帮助你更好地管理项目结构。

4.1. 使用 baseUrlpaths 简化模块导入

在大型项目中,模块导入路径可能会很长,例如:

import { Utils } from "../../../utils/utils";

这不仅难以阅读,而且在移动文件时需要修改大量的导入路径。我们可以使用 baseUrlpaths 来简化模块导入。

  • baseUrl: 指定模块解析的基准路径。
  • paths: 指定模块路径的映射。
{
  "compilerOptions": {
    // ...
    "baseUrl": "./src",
    "paths": {
      "@utils/*": ["utils/*"],
      "@components/*": ["components/*"]
    }
  }
}

配置了 baseUrlpaths 后,我们可以这样导入模块:

import { Utils } from "@utils/utils";
import { Button } from "@components/button";

这不仅更加简洁,而且在移动文件时,只需要修改 tsconfig.json 中的 paths 配置即可。

4.2. 使用 references 构建多项目

在大型项目中,我们可能会将项目拆分成多个子项目,每个子项目都有自己的 tsconfig.json 文件。references 可以让我们将这些子项目组织起来,构建一个完整的项目。

  • references: 指定引用的其他 tsconfig.json 文件。

例如,我们有两个子项目:frontendbackend。它们的 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"]
  }
}

注意 compositedeclaration 必须为 true。

这样,当我们编译顶层的 tsconfig.json 文件时,TypeScript 编译器会自动编译 frontendbackend 两个子项目。

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. 最佳实践总结

  • 精确指定 includeexclude,减少文件搜索范围。
  • 开启增量编译,提升编译速度。
  • 如果确定第三方库没有类型错误,可以跳过对它们的类型检查。
  • 使用 node 模块解析策略。
  • 使用 baseUrlpaths 简化模块导入。
  • 使用 references 构建多项目。
  • 开启 strict 模式,保证代码质量。
  • 定期检查 tsconfig.json,根据项目需求进行调整。

7. 结语

tsconfig.json 的配置是一个持续学习和优化的过程。希望今天的分享能帮助你更好地理解和使用 tsconfig.json,提升你的 TypeScript 项目的编译速度和项目结构管理能力。记住,没有最好的配置,只有最适合你的配置!

下次再见,各位!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注