什么是TypeScript中的References 工程引用?

614 阅读4分钟

TypeScript 3.0 的 References 工程引用--build 模式

TypeScript 3.0 引入了 References 工程引用--build 模式,工程引用是TypeScript 3.0的新特性,它支持将TypeScript程序的结构分割成更小的组成部分。这样可以改善构建时间,强制在逻辑上对组件进行分离,更好地组织你的代码。--build标记,它与工程引用协同工作可以加速TypeScript的构建。


Vite 项目中 tsconfig.jsontsconfig.app.jsontsconfig.node.json 的结构

使用npm init vite@latest创建的 Vue3 项目中,会有3个tsconfig.json文件。分别为tsconfig.jsontsconfig.app.jsontsconfig.node.json

通常用于区分不同的环境配置,提高可维护性和灵活性。这种结构一般用于区分 前端(应用层)后端(Node.js 层),或者在构建开发阶段分别使用不同的配置。

1. 各个配置文件的含义

1.1 tsconfig.json主配置文件

  • 示例
    {
    "files": [],
    "references": [
      { "path": "./tsconfig.app.json" },
      { "path": "./tsconfig.node.json" }
    ]
    }
    
    

references:指定了 tsconfig.app.json 和 tsconfig.node.json 作为依赖,确保这些文件中的配置会被合并并执行。

1.2 tsconfig.app.json

用于前端应用配置,支持 Vue 3 和 TypeScript,启用了严格模式和增量编译。

  • 示例
    {
    "extends": "@vue/tsconfig/tsconfig.dom.json",
    "compilerOptions": {
      "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
    
      /* Linting */
      "strict": true,
      "noUnusedLocals": true,
      "noUnusedParameters": true,
      "noFallthroughCasesInSwitch": true,
      "noUncheckedSideEffectImports": true
    },
    "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
    }
    

extends:继承了 @vue/tsconfig/tsconfig.dom.json,这是 Vue 官方推荐的基础配置,专为 Vue 3 项目定制,提供了对 Vue 文件的支持。

compilerOptions.tsBuildInfoFile:指定了 TypeScript 构建的增量编译信息文件位置。

1.3 tsconfig.node.json

用于 Node.js 环境的配置,针对 Vite 和服务器端代码优化了 TypeScript 设置。这里是指vite打开的本地服务器。

  • 示例
    {
      "compilerOptions": {
      "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
      "target": "ES2022",
      "lib": ["ES2023"],
      "module": "ESNext",
      "skipLibCheck": true,
    
      /* Bundler mode */
      "moduleResolution": "bundler",
      "allowImportingTsExtensions": true,
      "isolatedModules": true,
      "moduleDetection": "force",
      "noEmit": true,
    
      /* Linting */
      "strict": true,
      "noUnusedLocals": true,
      "noUnusedParameters": true,
      "noFallthroughCasesInSwitch": true,
      "noUncheckedSideEffectImports": true
      },
      "include": ["vite.config.ts"]
    }
    
    

为什么要拆分多个配置文件?

  1. 环境差异
  • 前端和后端使用不同的模块系统(ESModules vs CommonJS)。
  • 需要不同的构建工具配置(如 Vite 和 Node.js)。
  1. 构建效率
    这种配置文件结构有助于保持前后端代码独立,并通过增量编译提高构建效率。

使用 --buildReferences 工程引用 的优势

1. 大型单体仓库(Monorepo)

  • 场景:多个前端项目或子模块共享公共库、工具函数或组件库。
    例如,一个大型电商平台可能有多个独立模块(用户管理、订单管理、支付系统等),但共享一些公共组件库或工具包。
  • 优势
    • 每个子项目或库有独立的 tsconfig.json,并通过 references 配置管理依赖。
    • 使用 --build 增量构建,避免全局重新编译,提高构建效率。
    • 清晰的依赖关系和编译边界,减少编译时间。

2. 多包项目(例如 npm 包开发)

  • 场景:开发多个 npm 包,每个包都有独立的 tsconfig.json,但共享通用的类型定义或配置。
    例如,一个 UI 组件库,将不同的组件分为独立的包(如按钮、表格、下拉菜单),但共享公共类型定义。
  • 优势
    • 每个包可以独立发布和版本管理。
    • 通过 --buildreferences 配置,进行依赖管理和高效增量构建。

使用 project references 示例

项目结构

/my-project
  /module-a
    tsconfig.json
    index.ts
  /module-b
    tsconfig.json
    index.ts
  tsconfig.json

主项目 tsconfig.json

在主项目的 tsconfig.json 中,启用 composite,并使用 references 来引用各个子模块(如 module-amodule-b)。

// root-level tsconfig.json
{
  "files": [],
  "references": [
    { "path": "./module-a" },
    { "path": "./module-b" }
  ]
}
//tsconfig.base.json
{
  "compilerOptions": {
    "declaration": true,
    "target": "es5",
    "module": "commonjs",
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "composite": true,
  }
}
  • composite:启用增量编译。
  • references:指定模块的依赖关系,TypeScript 会按顺序编译这些模块。

module-atsconfig.json

// module-a/tsconfig.json
{
  "extends": "../tsconfig.base.json",
  "compilerOptions": {
    "rootDir": ".",
    "outDir": "../dist/module-a"
  },
  "include": ["index.ts"]
}
  • rootDir:指定源代码的根目录。
  • outDir:编译输出目录。
  • include:指定要编译的文件。

module-a/index.ts

// module-a/index.ts
export function add(a: number, b: number): number {
  return a + b;
}

module-btsconfig.json

module-b 引用了 module-a,这意味着在编译 module-b 时,module-a 会先被编译。

// module-b/tsconfig.json
{
  "extends": "../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "../dist/module-b",
    "rootDir": "."
  },
  "include": ["index.ts"],
  "references": [
    { "path": "../module-a" }  // 引用 module-a
  ]
}

module-b/index.ts

// module-b/index.ts
import { add } from "../module-a/index.js";

export function calculate(a: number, b: number): number {
  return add(a, b) * 2;  // 使用 module-a 中的 add 函数
}

构建并运行

  1. 使用 tsc --build 命令进行构建:
tsc --build
  1. 运行编译后的代码:
node dist/module-b/index.js

控制台输出

6

注意点:

  • 使用tsc -b --verbose可以查看构建流程。包括构建文件顺序,哪些文件被重新编译,哪些文件跳过编译(因为没有变化)等。
  • 使用tsc进行ts编译并不能直接将module-b的引用路径转化为正确的路径。因此需要保证构建产物仍然保持与源文件一致的文件结构。

结论

  • 使用 References 工程引用--build 模式 可以极大提升大型 TypeScript 项目的编译效率,特别是在多模块或多包的情况下。
  • Vite创建的Vue项目中会有三个tsconfig,tsconfig.jsontsconfig.app.jsontsconfig.node.json 的拆分,可以帮助区分不同环境的 TypeScript 配置,确保不同环境下的编译需求和工具设置不冲突。