我的编程空间,编程开发者的网络收藏夹
学习永远不晚

tsc性能优化ProjectReferences使用详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

tsc性能优化ProjectReferences使用详解

什么是 Project References

在了解一个东西是什么的时候,直接看其官方定义是最直观的

TypeScript: Documentation中对于Project References的介绍如下:

Project references are a new feature in TypeScript 3.0 that allow you to structure your TypeScript programs into smaller pieces.

这是TypeScript 3.0新增的特性,这个特性有啥用呢?

将我们的项目分成多个小的片段,也就是允许我们将项目进行分包模块化

这样一来我们在执行tsc对项目进行构建的时候,无论是出于代码转译成js的目的还是说出于单纯的类型检查(将compilerOptions.noEmit置为true)的目的

都可以严格按照自己的需要对想要被tsc处理的那部分代码进行处理,而不是每次都对整个项目进行处理,这是我认为Project References的最大好处

就以一个前后端代码在同一个仓库里维护的项目为例,如果没有Project References,那么我执行tsc时,会对前后端模块都进行类型检查和转译

但实际上如果我只修改了前端部分的代码,理所应当让tsc只处理前端模块,后端模块的构建产物不需要进行重新构建,有了Project References,我们就能实现到这个需求,从而优化项目的构建或类型检查性能

相信大家对Project References有一个大概的认识了,接下来我们就开始实际动手体验一下Project References加深我们对它的理解吧!

示例项目结构

.
├── package.json
├── pnpm-lock.yaml
├── class="lazy" data-src
│   ├── __test__              // 单元测试
│   │   ├── client.test.ts    // 前端代码测试
│   │   ├── index.ts          // 简陋的单元测试 API
│   │   └── server.test.ts    // 后端代码测试
│   ├── client                // 前端模块
│   │   └── index.ts
│   ├── server                // 后端模块
│   │   └── index.ts
│   └── shared                // 共享模块 -- 包含通用的工具函数
│       └── index.ts
└── tsconfig.json             // TypeScript 配置

这是一个很常见的项目目录结构,有前端代码,有后端代码,也有通用工具函数代码以及前后端的单元测试代码

它们的依赖关系如下:

  • client 依赖 shared
  • server 依赖 shared
  • __test__ 依赖 client 和 server
  • shared 无依赖

不使用 Project References 带来的问题

现在整个项目只有一个tsconfig.json位于项目根目录下,其内容如下:

{
  "compilerOptions": {
    "target": "ES5",
    "module": "CommonJS",
    "strict": true,
    "outDir": "./dist"
  }
}

如果我们执行tsc,它会将各个模块的代码都打包到项目根目录的dist目录下

dist
├── __test__
│   ├── client.test.js
│   ├── index.js
│   └── server.test.js
├── client
│   └── index.js
├── server
│   └── index.js
└── shared
    └── index.js

这有一个很明显的问题,正如前面所说,当我们只修改一个模块,比如只修改了前端模块的代码,那么理应只需要再构建前端模块的产物即可,但是无论改动范围如何,都是会将整个项目都构建一次,这在项目规模变得越来越大的时候会带来极大的性能问题,构建时长会变得特别长

或许你会想着在每个模块里创建一个tsconfig.json,然后通过tsc -p指定每个模块的目录去单独对它们进行构建,没错,这是一种解决方案

但是这会带来下面两个问题:

  • 如果需要全量构建项目,你得需要运行三次tsc,对每个模块分别构建,而tsc的启动时间开销是比较大的,在这个小规模项目里甚至启动开销的时间比实际构建的时间更长,现在还只是运行三次tsc,如果项目模块很多,有几十上百个呢?那光是启动tsc几十上百次都已经会花一些时间了
  • tsc -w不能一次监控多个tsconfig.json,只能是对各个模块都启动一次tsc -w

Project References的出现,就是为了解决上述问题的

tsconfig.json 的 references 配置项

Project References就是tsconfig.json里的references配置项,其结构是一个包含若干对象的数组,对象的结构如下:

{
  "references": [{ "path": "path/to/referenced-project" }]
}

核心就是一个path属性,该属性指向被引用的项目模块路径,该路径下需要包含tsconfig.json,如果该模块不是用tsconfig.json命名的话,你也可以指定具体的文件名,比如:

{
  "references": [{ "path": "path/to/referenced-project/tsconfig.web.json" }]
}

当指定了references选项后,会发生如下改变:

  • 在主模块中导入被引用的模块时,会加载它的类型声明文件,也就是.d.ts后缀的文件
  • 使用tsc --buildtsc -b构建主模块时,会自动构建被引用的模块

这样一来能够带来三个好处:

  • 提升类型检查和构建的速度
  • 减少IDE的运行内存占用
  • 更容易对项目结构进行划分

tsconfig.json 的 composite 配置项

光是在主模块中指定references配置项还不够,还需要在被引用的项目对应的tsconfig.json中开启composite配置项

composite配置项又是干嘛的呢? -- 它可以帮助tsc快速确定如何寻找被引用项目的输出产物

当被引用的项目开启composite配置项后,会有如下改变和要求:

当未指定rootDir时,默认值不再是The longest common path of all non-declaration input files,而是包含了tsconfig.json的目录

Tips: 关于The longest common path of all non-declaration input files的意思可以到tsconfig.json 文章中关于 rootDir 的介绍中查阅

必须开启include或者files配置项将要参与构建的文件声明进来

必须开启declaration配置项(因为前面介绍references的时候说了,会加载被引入模块的类型声明文件,因此被引用模块自然得开启declaration配置项生成自己的类型声明文件供主模块加载)

使用 Project References 改造示例项目

根据目前我们对Project References的认识,现在可以开始改造一下我们的项目了,首先是根目录下的tsconfig.json配置,它起到一个类似于项目入口的作用,因此这里面只负责添加references声明项目中需要被构建的模块,以及通过exclude将不需要参与构建的模块排除(比如class="lazy" data-src/__test__中的测试代码)

/tsconfig.json

{
  "references": [
    { "path": "class="lazy" data-src/client" },
    { "path": "class="lazy" data-src/server" },
    { "path": "class="lazy" data-src/shared" }
  ],
  "exclude": ["**/__test__"]
}

然后是各个子模块的tsconfig.json配置,这里我们假设构建目标为es5的代码,所以对于clientserver以及shared来说是存在公共配置的,所以我们可以抽离出一个公共配置,然后在子模块中通过extends配置项公用一个配置

/tsconfig.base.json

{
  "compilerOptions": {
    "target": "ES5",
    "module": "CommonJS",
    "strict": true
  }
}

class="lazy" data-src/client/tsconfig.json

{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "../../dist/client",
    "composite": true,
    "declaration": true
  },
  // 依赖哪个模块则引用哪个模块
  "references": [{ "path": "../shared" }]
}

class="lazy" data-src/server/tsconfig.json

{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "../../dist/server",
    "composite": true,
    "declaration": true
  },
  // 依赖哪个模块则引用哪个模块
  "references": [{ "path": "../shared" }]
}

class="lazy" data-src/shared/tsconfig.json

{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "../../dist/shared",
    "composite": true,
    "declaration": true
  }
}

全量构建

现在我们在项目根目录下运行tsc --build --verbose,就会根据references配置去寻找各个子模块,并对它们进行构建,可以理解为对项目的全量构建

--build 参数表示让tscbuild模式进行构建和类型检查,也就是会使用references配置项,如果不开启的话是不会使用references配置项的,这点可以从官方文档中得证:

--verbose 参数则是会将构建过程中的输出显示在控制台中,不开启该参数的话则不会显示输出(除非构建过程中报错)

运行后/dist目录结构如下

dist
├── client
│   ├── index.d.ts
│   ├── index.js
│   └── tsconfig.tsbuildinfo
├── server
│   ├── index.d.ts
│   ├── index.js
│   └── tsconfig.tsbuildinfo
└── shared
    ├── index.d.ts
    ├── index.js
    └── tsconfig.tsbuildinfo

可以看到,所有子模块都被构建进来了,各模块的产物中有一个tsconfig.tsbuildinfo文件,这个文件起到一个类似缓存的作用,对于后续进行增量构建有着重要作用

前面看到的官方文档中对tsc --build的作用的介绍中的第二点Detect if they are up-to-date主要就是依靠这个缓存文件去识别的

开启--verbose参数后,可以看到控制台输出如下:

[4:50:26 PM] Projects in this build:
    * class="lazy" data-src/shared/tsconfig.json
    * class="lazy" data-src/client/tsconfig.json
    * class="lazy" data-src/server/tsconfig.json
    * tsconfig.json
[4:50:26 PM] Project 'class="lazy" data-src/shared/tsconfig.json' is out of date because output file 'dist/shared/tsconfig.tsbuildinfo' does not exist
[4:50:26 PM] Building project '/home/plasticine/demo/ts-reference-demo/class="lazy" data-src/shared/tsconfig.json'...
[4:50:28 PM] Project 'class="lazy" data-src/client/tsconfig.json' is out of date because output file 'dist/client/tsconfig.tsbuildinfo' does not exist
[4:50:28 PM] Building project '/home/plasticine/demo/ts-reference-demo/class="lazy" data-src/client/tsconfig.json'...
[4:50:28 PM] Project 'class="lazy" data-src/server/tsconfig.json' is out of date because output file 'dist/server/tsconfig.tsbuildinfo' does not exist
[4:50:28 PM] Building project '/home/plasticine/demo/ts-reference-demo/class="lazy" data-src/server/tsconfig.json'...
[4:50:29 PM] Project 'tsconfig.json' is out of date because output 'class="lazy" data-src/shared/index.js' is older than input 'class="lazy" data-src/client'
[4:50:29 PM] Building project '/home/plasticine/demo/ts-reference-demo/tsconfig.json'...
[4:50:29 PM] Updating unchanged output timestamps of project '/home/plasticine/demo/ts-reference-demo/tsconfig.json'...

xxx out of date xxx意思就是这个模块没被构建过,因此会开始对其进行构建

由于我们是首次构建,所以三个模块都是没被构建过的,所以三个模块都被检测为out of date

当我们再次运行tsc --build --verbose时,输出如下:

4:54:35 PM - Projects in this build:
    * class="lazy" data-src/shared/tsconfig.json
    * class="lazy" data-src/client/tsconfig.json
    * class="lazy" data-src/server/tsconfig.json
    * tsconfig.json
4:54:35 PM - Project 'class="lazy" data-src/shared/tsconfig.json' is up to date because newest input 'class="lazy" data-src/shared/index.ts' is older than output 'dist/shared/tsconfig.tsbuildinfo'
4:54:35 PM - Project 'class="lazy" data-src/client/tsconfig.json' is up to date because newest input 'class="lazy" data-src/client/index.ts' is older than output 'dist/client/tsconfig.tsbuildinfo'
4:54:35 PM - Project 'class="lazy" data-src/server/tsconfig.json' is up to date because newest input 'class="lazy" data-src/server/index.ts' is older than output 'dist/server/tsconfig.tsbuildinfo'
4:54:35 PM - Project 'tsconfig.json' is up to date because newest input 'dist/server/index.d.ts' is older than output 'class="lazy" data-src/client/index.js'

可以看到,所有模块都被检测为up to date,从而避免了重复构建

增量构建

如果现在我们修改了client模块的代码,再运行tsc --build --verbose会怎样呢?估计你也能猜到了,只有client模块会被构建,而其他模块则会跳过

4:56:44 PM - Projects in this build:
    * class="lazy" data-src/shared/tsconfig.json
    * class="lazy" data-src/client/tsconfig.json
    * class="lazy" data-src/server/tsconfig.json
    * tsconfig.json
4:56:44 PM - Project 'class="lazy" data-src/shared/tsconfig.json' is up to date because newest input 'class="lazy" data-src/shared/index.ts' is older than output 'dist/shared/tsconfig.tsbuildinfo'
4:56:44 PM - Project 'class="lazy" data-src/client/tsconfig.json' is out of date because output 'dist/client/tsconfig.tsbuildinfo' is older than input 'class="lazy" data-src/client/index.ts'
4:56:44 PM - Building project '/home/plasticine/demo/ts-reference-demo/class="lazy" data-src/client/tsconfig.json'...
4:56:45 PM - Project 'class="lazy" data-src/server/tsconfig.json' is up to date because newest input 'class="lazy" data-src/server/index.ts' is older than output 'dist/server/tsconfig.tsbuildinfo'
4:56:45 PM - Project 'tsconfig.json' is out of date because output file 'class="lazy" data-src/client/index.js' does not exist
4:56:45 PM - Building project '/home/plasticine/demo/ts-reference-demo/tsconfig.json'...
4:56:45 PM - Updating unchanged output timestamps of project '/home/plasticine/demo/ts-reference-demo/tsconfig.json'...

相信现在你能体会到Project References的好处了吧,能够很大程度上优化我们的构建速度!

不过实际开发中,tsc更多的是用来进行类型检查,至于compile的工作,则更多地是交给如Babelswcesbuild等工具去完成,这也是官方文档中有提到过的

这也是为什么你在vite创建的项目中能够看到默认的build命令配置为tsc && vite build,正是将类型检查的工作交给tsc,而构建工作则交给vite底层依赖的rollup去完成

对__test__测试代码的处理

我们的改造貌似已经完成了,但其实还忽略了一个class="lazy" data-src/__test__,它也可以被视为一个模块,它作为主模块,依赖了clientserver,因此也可以给它加上tsconfig.json配置,并且对于测试代码,我们一般不希望将它们构建成js,只希望tsc负责类型检查的工作,因此我们需要进行如下配置:

class="lazy" data-src/__test__/tsconfig.json

{
  "compilerOptions": {
    "noEmit": true
  },
  "references": [{ "path": "../client" }, { "path": "../server" }]
}

noEmit的作用刚刚在官方文档中也看到了,不会把产物文件输出,如果我们只需要类型检查能力的话很适合开启该配置项

现在我们如果需要对__test__中的代码进行类型检查的话,只需要执行:

# 忽略 references 配置项
tsc --project class="lazy" data-src/__test__
# 启用 references 配置项
tsc --build class="lazy" data-src/__test__

如果是使用--project参数的话,tsconfig.json中可以忽略references配置项,因为即便配置了也不会被使用,这在依赖产物未构建出来时能起作用

而如果使用--build参数,并且clientserver未构建出来时,会先构建它们,再对测试代码进行类型检查,可以根据个人需求场景来决定使用--project还是--build

总结

本篇文章介绍了Project References是什么,并通过一个简单的示例项目,并结合TypeScript Documentation官方文档边实战边解释

总的来说,其使用起来就是:

  • 主模块(tsc --build作用的模块视为主模块)中通过references配置项声明依赖的模块
  • 被引用模块中开启compositedeclaration配置项以支持被引用
  • 通过tsc --build 主模块才可以启用references配置项,这在官方文档中被称为Build Mode,如果直接tsc 主模块的话,是不会启用references配置项的,也就导致依然会对项目中的所有ts文件进行编译(如果没配置includefiles配置项的话)

希望通过本篇文章,能够让你对Project References有一个全面了解,也希望能够将其用在你的项目中,提升类型检查或构建(使用 tsc 进行构建的话)的速度,更多关于tsc性能Project References的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

tsc性能优化ProjectReferences使用详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

tsc性能优化ProjectReferences使用详解

这篇文章主要为大家介绍了tsc性能优化ProjectReferences使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-16

Android TraceView和Lint使用详解及性能优化

Android lint工具是Android studio中集成的一个代码提示工具,它主要负责对你的代码进行优化提示,包括xml和java文件,很强大。编写完代码及时进行lint测试,会让我们的代码变得非常规范而且避免代码冗余。让我们及时发
2022-06-06

React.memo React.useMemo对项目性能优化使用详解

这篇文章主要为大家介绍了React.memo React.useMemo对项目性能优化的使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-01-17

Android性能优化之弱网优化详解

这篇文章主要为大家介绍了Android性能优化之弱网优化示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

详解GaussDB for MySQL性能优化

目录背景灵感来源于生活快递的优化原理GaussDB(for MySQL)的优化实际测试背景 我们先来看看MySQL 8.0的事务提交的大致流程以上流程,是MySQL8.0对WAL原则的一种实现,这个流程意味着,任何一个事务的提交,一定要完成
2022-05-18

Android性能优化之Bitmap图片优化详解

前言 在Android开发过程中,Bitmap往往会给开发者带来一些困扰,因为对Bitmap操作不慎,就容易造成OOM(Java.lang.OutofMemoryError - 内存溢出),本篇博客,我们将一起探讨Bitmap的性能优化。
2022-06-06

C++ 函数继承详解:如何使用继承优化性能?

重载允许定义同名函数以优化性能,不同参数触发不同实现。为不同形状(矩形、圆形)定义了一个抽象 shape 类,利用子类 rectangle 和 circle 重载了 area() 方法,通过形状类型自动调用正确的实现,避免冗余计算。C++
C++ 函数继承详解:如何使用继承优化性能?
2024-05-05

JS 加载性能Tree Shaking优化详解

这篇文章主要为大家介绍了JS 加载性能Tree Shaking优化详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-16

PHP 性能优化:缓存机制详解

php 缓存机制通过在内存中存储数据来提高网站性能,主要有三种类型:内存缓存(极快读取)、文件缓存(持久)、对象缓存(自定义序列化)。实战案例包括使用 apc 进行内存缓存、memcached 进行分布式缓存和 redis 进行对象缓存。最
PHP 性能优化:缓存机制详解
2024-05-10

C++ 函数优化详解:如何优化多线程性能?

优化多线程 c++++ 函数性能的关键技术包括:编译器优化标志(例如 -o3 和 -parallel)并发容器(例如 std::vector 和 std::list)同步原语(例如锁和原子变量)智能指针(例如 std::shared_ptr
C++ 函数优化详解:如何优化多线程性能?
2024-05-04

MySQL Explain 详解(优化MySQL性能第一步)

MySQL Explain 详解 使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句。分析你的查询预付或表结构的性能瓶颈。 查询结果返回字段分析 1、id列 select查询的序列号,包
MySQL Explain 详解(优化MySQL性能第一步)
2014-08-20

React数据获取与性能优化详解

这篇文章主要为大家介绍了React数据获取与性能优化方法示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

详解Android性能优化之内存泄漏

综述 内存泄漏(memory leak)是指由于疏忽或错误造成程序未能释放已经不再使用的内存。那么在Android中,当一个对象持有Activity的引用,如果该对象不能被系统回收,那么当这个Activity不再使用时,这个Activity
2022-06-06

React性能优化的实现方法详解

react凭借virtualDOM和diff算法拥有高效的性能,除此之外也有很多其他的方法和技巧可以进一步提升react性能,在本文中我将列举出可有效提升react性能的几种方法,帮助我们改进react代码,提升性能
2023-01-30

C++ 函数优化详解:如何优化输入输出性能?

通过以下优化技术可提高 c++++ 中的输入输出性能:1. 使用文件指针;2. 使用流;3. 使用缓存;4. 优化 i/o 操作(批量 i/o、异步 i/o、内存映射 i/o)。C++ 函数优化详解:如何优化输入输出性能?输入输出 (I/
C++ 函数优化详解:如何优化输入输出性能?
2024-05-04

编程热搜

目录