原文:https://github.com/microsoft/…
豆皮粉儿们,又又又见面了,明天这一期,由字节跳动数据平台的“StoneyAllen
”,给大家翻译一篇文章“typescript 性能”。在 2020 年倒计时最初几天我要祝大家新年快乐~,打工人,打工魂~。上面就开始仔细阅读吧!
翻译者:StoneyAllen
有些简略的 Typescript 配置,能够让你取得更快的编译和编辑体验,这些办法越早把握越好。上面列举了除了最佳实际以外,还有一些用于考察迟缓的编译 / 编辑体验的罕用技术,以及一些作为最初伎俩来帮忙 TypeScript 团队考察问题的罕用办法。
编写易编译代码
优先应用接口而不是穿插类型
很多时候,简略对象类型的类型别名与接口的作用十分类似
然而,只有你须要定义两个及以上的类型,你就能够选用接口来扩大这些类型,或者在类型别名中对它们相交,这时差别就变得显著了。
因为接口定义的是繁多立体对象类型,能够检测属性是否抵触,解决这些抵触是十分必要的。另一方面,穿插类型只是递归的合并属性,有些状况下会产生 never。接口则体现的一贯很好,而穿插类型定义的类型别名不能显示在其余的穿插类型上。接口之间的类型关系也会被缓存,而不是整个穿插类型。最初值得注意的区别是,如果是穿插类型,会在查看“无效”/“展平”类型之前查看所有属性。
因而,倡议在创立穿插类型时应用带有接口 / 扩大的扩大类型。
应用类型正文
增加类型正文,尤其是返回类型,能够节俭编译器的大量工作。这是因为命名类型比匿名类型更简洁(编译器更喜爱),这缩小了大量的读写申明文件的工夫。尽管类型推导是十分不便的,没有必要到处这么做。然而,如果您晓得了代码的慢速局部,可能会很有用。
优先应用根底类型而不是联结类型
联结类型十分好用 – 它能够让你表白一种类型的可能值范畴。
然而他们也带来了肯定开销。每次将参数传递给 printSchedule
时,须要比拟联结类型里的每个元素。对于一个由两个元素组成的联结类型来说,这是微不足道的。然而,如果你的联结类型有很多元素,这将引起编译速度的问题。例如,从联结类型中淘汰多余的局部,元素须要成对的去比拟,工作量是呈二次递增的。当大量联结类型穿插一起时产生这种查看,会在每个联结类型上相交导致大量的类型,须要缩小这种状况产生。防止这种状况的一种办法是应用子类型,而不是联结类型。
一个更事实的例子是,定义每种内置 DOM 元素的类型时。这种状况下,更优雅的形式是创立一个蕴含所有元素的 HtmlElement
根底类型,其中包含 DivElement
、ImgElement
等。应用继承而不是创立一个无穷多的联结类型 DivElement | /*...*/ | ImgElement | /*...*/
。
应用我的项目援用
应用 TypeScript 构建内容较多的代码时,将代码库组织成几个独立的我的项目会很有用。每个我的项目都有本人的 tsconfig.json
,可能它会对其余我的项目有依赖性。这有益于防止在一次编译中导入太多文件,也使某些代码库布局策略更容易地放在一起。
有一些十分根本的办法将一个代码库分解成多个我的项目。举个例子,一个程序代码,一部分用作客户端,一部分用作服务端,另一部分被其它两个共享
测试也能够合成到本人的我的项目中
一个常见的问题是 “ 一个我的项目应该有多大?”。这很像问 “ 一个函数应该有多大?” 或 “ 一个类应该有多大?”,在很大水平上,这归纳于教训。人们相熟的一种宰割 JS/TS 代码的办法是应用文件夹。作为一种启发式的办法,如果它们关联性足够大,能够放在同一个文件夹中,那么它们就属于同一个我的项目。除此之外,要避免出现极大或极小规模的我的项目。如果一个我的项目比其余所有我的项目加起来都要大,那就是一个正告信号。同样,最好防止有几十个单文件我的项目,因为也会减少开销。
你能够在这里浏览更多对于我的项目参考资料
配置 tsconfig.json 或 jsconfig.json
TypeScript
和 JavaScript
用户能够用 tsconfig.json
文件任意配置编译形式。JavaScript
用户也能够应用 jsconfig.json
文件配置本人的编辑体验。
指定文件
你应该始终确保你的配置文件没有蕴含太多文件
在 tsconfig.json
中,有两种形式能够指定我的项目中的文件
- files 列表
- include、exclude 列表
两者的次要区别是,files
冀望失去一个源文件的文件门路列表,而 include/exclude
应用通配符模式对文件进行匹配
尽管指定文件能够让 TypeScript
间接疾速地加载文件,但如果你的我的项目中有很多文件,而不只是几个顶层的入口,那就会很麻烦。此外,很容易遗记增加新文件到 tsconfig.json
中,这意味着你可能最终会失去奇怪的编辑器行为,这些新文件被谬误地剖析,这些都很辣手。
include/exclude
有助于防止指定这些文件,但代价是:必须通过 include
蕴含的目录来发现文件。当运行大量的文件夹时,这可能会减慢编译速度。此外,有时编译会蕴含很多不必要的 .d.ts
文件和测试文件,这会减少编译工夫和内存开销。最初,尽管 exclude
有一些正当的默认值,但某些配置比方 mono-repos
,意味着像node_modules
这样的 “ 重 “ 文件夹依然能够最终被蕴含。
对于最佳做法,咱们倡议如下:
- 在您的我的项目中只指定输出文件夹(即您想将其源代码蕴含在编译 / 剖析中的文件夹)
- 不要把其余我的项目的源文件混在同一个文件夹里
- 如果把测试和其余源文件放在同一个文件夹里,请给它们取一个不同的名字,这样就能够很容易地把它们排除在外
- 防止在源目录中呈现大的构建工件和依赖文件夹,如
node_modules
留神:如果没有排除列表,默认状况下 node_modules 是被排除的;一旦增加了 node_modules,就必须明确地将 node_modules 增加到列表中。
上面是一个正当的tsconfig.json
,用来演示这个操作
管制蕴含的 @types
默认状况下,TypeScript
会主动蕴含每一个在 node_modules
文件夹中找到的 @types
包,不论你是否导入它。这是为了在应用 Node.js、Jasmine、Mocha、Chai 等工具 / 包时,使某些货色 “ 可能工作 ”,因为这些工具 / 包没有被导入 – 它们只是被加载到全局环境中
有时这种逻辑在编译和编辑场景下都会拖慢程序的构建工夫,甚至会造成多个全局包的申明抵触的问题,造成相似于如下问题
在不须要全局包的状况下,修复办法很简略,只有在 tsconfig.json/jsconfig.json
中为 “type “ 选项指定一个空字段即可。
如果您依然须要一些全局包,请将它们增加到类型字段中
增量我的项目输入
--incremental
标记容许 TypeScript 将上次编译的状态保留到一个 .tsbuildinfo
文件中。这个文件用来计算上次运行后可能被从新查看 / 从新输入的最小文件集,就像 TypeScript 的 --watch
模式一样。
当对我的项目援用应用复合标记时,默认状况下会启用增量编译,但这样也能带来同样的速度晋升。
跳过 .d.ts 查看
默认状况下,TypeScript 会对一个我的项目中的所有 .d.ts
文件进行全面查看,以发现问题或不统一的中央;然而,这查看通常是不必要的。大多数时候,.d.ts
文件都是已知如何工作的 – 类型之间互相扩大的形式曾经被验证过一次,重要的申明还是会被查看。
TypeScript 提供了一个选项,应用 skipDefaultLibCheck
标记来跳过 .d.ts
文件的类型查看(例如lib.d.ts
)
另外,你也能够启用 skipLibCheck
标记来跳过编译中的所有 .d.ts
文件
这两个选项通常会暗藏 .d.ts
文件中的谬误配置和抵触,所以只倡议在疾速构建场景中应用它们。
应用更快的差别查看
狗的列表是动物的列表吗?也就是说,List<Dog>
是否能够调配给 List<Animals>
?寻找答案的间接办法是一一成员进行类型构造比拟。可怜的是,这可能带来低廉的性能开销。然而,如果咱们对List<T>
有足够的理解,咱们能够将这个可调配性查看简化为确定 Dog,是否能够调配给 Animal(即不思考 List<T>
的每个成员)。特地是,当咱们须要晓得类型参数 T 的差异。编译器只有在启用 strictFunctionTypes
标记的状况下,能力充分利用这种潜在的减速劣势(否则,它就会应用较慢的,但更宽松的构造查看)。因而,咱们倡议应用 --strictFunctionTypes
来构建(默认在 --strict
下启用)
配置其余构建工具
TypeScript 编译常常与其余构建工具一起执行 – 特地是在编写可能波及捆绑程序的 Web 应用程序时。尽管咱们只能对一些构建工具提出倡议,但现实状况下,这些技术能够被遍及。
确保除了浏览本节外,你还浏览了对于你所抉择的构建工具的性能 – 例如:
- ts-loader 的 Faster Builds 局部
- awesome-typescript-loader 的性能问题局部
并行类型查看
类型查看通常须要从其余文件中获取信息,与转换 / 输入代码等其余步骤相比,类型查看可能绝对低廉。因为类型查看可能会破费更多的工夫,它可能会影响到外部的开发循环 – 换句话说,你可能会经验更长的编辑 / 编译 / 运行周期,这可能会令你头疼。
出于这个起因,一些构建工具能够在一个独自的过程中运行类型查看,而不会阻塞输入。尽管这意味着在 TypeScript 构建而产生错误报告之前曾经有有效的代码运行,通常会先在编辑器中看到谬误,而不会被长时间地阻止运行工作代码
一个理论的例子是 Webpack 的 fork-ts-checker-webpack-plugin
插件,或者 awesome-typescript-loader
有时也会这样做。
隔离文件输入
默认状况下,TypeScript 输入须要的语义信息可能不是本地文件。这是为了了解如何输入像 const enums
和 namespaces
这样的性能。然而须要查看其余文件来生成某个文件,这会使输入速度变慢。
对须要非本地信息的性能需要是比拟少见的 – 惯例枚举能够用来代替 const
枚举,模块能够用来代替命名空间。鉴于此,TypeScript 提供了 isolatedModules
标记,以便在由非本地信息驱动的性能上报错。启用 isolatedModules
意味着你的代码库对于应用 TypeScript APIs
(如 transpileModule
)或代替编译器(如 Babel
)的工具是平安的。
举个例子,上面的代码在运行时无奈失常应用独立的文件转换,因为 const enum
值被冀望内联;侥幸的是,isolatedModules
会在晚期通知咱们这一点
记住:isolatedModules 不会主动让代码生成速度更快 – 它只是通知你,你行将应用一个可能不被反对的性能。你要的是独立模块在不同的构建工具和 API 中的输入
能够通过应用以下工具来影响独立文件的输入
- ts-loader 提供了一个 transpileOnly 标记,通过应用 transpileModule 来执行独立文件输入
- awesome-typescript-loader 提供了一个 transpileOnly 标记,通过应用 transpileModule 来执行独立文件输入
- TypeScript 能够间接应用 transpileModule API
- awesome-typescript-loader 提供了 useBabel 标记
- babel-loader 以独自的形式编译文件(但不提供类型查看)
- gulp-typescript 启用 isolatedModules 时,能够实现独立文件输入
- rollup-plugin-typescript 只执行独立文件编译
- ts-jest 能够应用(isolatedModules 标记设为 true)isolatedModules 为 true
- ts-node 能够检测 tsconfig.json 的 “ts-node “ 字段中的 “transpileOnly “ 选项,也有一个 –transpile-only 标记。
考察问题
有肯定的办法能够失去可能出问题的提醒
禁用编辑器插件
编辑器的体验受到插件的影响。尝试禁用插件(尤其是 JavaScript/TypeScript 相干的插件),看看是否能解决性能和响应速度方面的问题。
某些编辑器也有本人的性能故障排除指南,所以能够思考浏览一下。例如,Visual Studio Code
也有本人的性能问题介绍。
诊断扩大
你能够用 --extendedDiagnostics
来运行 TypeScript,以取得编译器破费工夫的打印日志。
请留神,总工夫不是后面所有工夫的总和,因为有一些重叠,有些工作是没有掂量工具的。
对于大多数用户来说,最相干的信息是:
思考到这些投入,你可能会想问一些问题:
- 文件数 / 代码行数是否与您我的项目中的文件数大抵统一?如果不合乎,请尝试运行
--listFiles
- 程序工夫或 I / O 读取工夫是否相当高?请确保你的 include/exclude 配置正确
-
其余工夫看起来不对劲吗?你可能想提出一个问题。你能够做以下事件来帮忙诊断
- 如果打印工夫较高,则应用
emitDeclarationOnly
运行 - 浏览对于报告编译器性能问题的阐明
- 如果打印工夫较高,则应用
显示配置
当运行 tsc 时,并不能显著地看到编译的内容设置,特地是思考到 tsconfig.jsons
能够扩大其余配置文件。showConfig
能够解释 tsc 将为一个调用计算着什么。
追踪分辨率
运行 traceResolution
能够有助于解释,一个文件为什么被蕴含在编译中。输入有点繁琐,所以你可能想把输入重定向到一个文件。
如果你发现了一个不应该存在的文件,你可能须要批改你的 tsconfig.json 中的 include/exclude 列表,或者,你可能须要调整其余设置,比方 type、typeRoots 或 paths。
独立运行 tsc
很多时候,用户在应用第三方构建工具(如 Gulp、Rollup、Webpack 等)时都会遇到性能迟缓的问题。运行 tsc --extendedDiagnostics
,能够发现 TypeScript 和工具之间的差别,用以阐明内部配置的谬误或效率低下。
一些须要留神的问题:
- tsc 和集成了 TypeScript 的构建工具在构建工夫上有很大的区别吗?
- 如果构建工具提供诊断,那么 TypeScript 的分辨率和构建工具的分辨率是否有区别?
- 构建工具是否有本人的配置,可能的起因是什么?
- 构建工具是否有可能是 TypeScript 集成的配置起因?(例如 ts-loader 的选项?)
降级依赖性
有时 TypeScript 的类型查看会受到计算密集的 .d.ts
文件的影响。这很常见也很可能会产生。降级到一个较新的 TypeScript 版本 (能够更有效率) 或一个较新版本的 @types 包 (可能曾经复原了一个回归) 通常能够解决这个问题。
常见的问题
一旦你曾经排除了故障,你可能想摸索一些常见问题的修复办法。如果以下解决方案不起作用,可能值得提出问题。
include 和 exclude 配置不当
如上所述,include/exclude 选项能够在以下几个方面被滥用
提出问题
如果你的我的项目曾经进行了正确的优化配置,你可能须要提出一个问题。
最好的性能问题报告蕴含容易取得的和最小的问题复制品。换句话说,一个容易通过 git 克隆的代码库,只蕴含几个文件。它们不须要与构建工具的内部集成 – 它们能够通过调用 tsc 或调用 TypeScript API 的独立代码。不优先思考那些须要简单调用和设置的代码库。
咱们了解这一点却不容易实现 – 特地是,很难在代码库中隔离问题的源头,而且共享知识产权可能也是一个问题。在某些状况下,如果咱们认为问题影响较大,团队将违心发送一份窃密协定(NDA)。
无论是否能够复制,在提交问题时,依照这些办法,将有助于为您提供性能修复。
报告编译器性能问题
有时,你会在构建工夫以及编辑场景中发现性能问题。在这种状况下,最好关注于 TypeScript 编译器。
首先,应该应用 TypeScript 的 next 版本,以确保你不会碰到那些已解决的问题。
一个编译器的问题可能包含
- 装置的 TypeScript 版本(例如:npx tsc -v 或 yarn tsc -v)
- TypeScript 运行的 Node 版本(例如:node -v)
- 应用 extendedDiagnostics 运行的输入(tsc –extendedDiagnostics -p tsconfig.json)
- 现实的状况是,一个我的项目可能展现所遇到的问题
- 分析编译器的输入日志(isolate-*-*-*.log 和 *.cpuprofile 文件)
分析编译器
通过应用 --trace-ic
标记与 --generateCpuProfile
标记,来让 TypeScript 运行 Node.js v10+,这对团队提供诊断后果来说是很重要的:
这里的 ./node_modules/typescript/lib/tsc.js 能够用来替换你的 TypeScript 编译器的装置版本,而 tsconfig.json 能够是任何 TypeScript 配置文件。profile.cpuprofile 是你抉择的输入文件。
这将产生两个文件:
--trace-ic
将输入到 isolate-*-*-*.log 的文件中(例如 isolate-00000176DB2DF130-17676-v8.log)--generateCpuProfile
将以您抉择的名称输入到一个文件中。在下面的例子中,它将是一个名为 profile.cpuprofile 的文件
正告:这些文件可能蕴含你的工作空间的信息,包含文件门路和源代码。这两个文件都能够作为纯文本浏览,您能够在将它们提交为 GitHub 问题之前批改它们。(例如,革除可能裸露外部专用信息的文件门路)。
然而,如果你对在 GitHub 上公开公布这些有任何顾虑,请通知咱们,能够私下分享细节。
报告编辑绩效问题
编辑性能常常受到很多货色的影响,TypeScript 团队惟一能管制的是 JavaScript/TypeScript 语言服务的性能,以及该语言服务和某些编辑器(即 Visual Studio、Visual Studio Code、Visual Studio for Mac 和 Sublime Text)之间的集成。确保所有第三方插件在编辑器中被敞开,以确定是否有 TypeScript 自身的问题。
编辑性能问题稍有波及,但同样的想法也实用于:可被克隆的最小重现代码库是现实的,尽管在某些状况下,团队将可能签订 NDA 来考察和隔离问题。
包含 tsc–extendedDiagnostics 的输入是很好的上下文,但取一个 TSServer 日志是最有用的。
收集 TSServer 日志
在 Visual Studio 代码中收集 TSServer 日志
-
关上你的命令菜单栏,而后抉择
- 进入 “ 首选项 “ 关上您的全局设置。关上用户设置
- 入偏好设置,关上本地我的项目。关上工作区设置
- 设置选项 “typecript.tsserver.log”:”verbose”
- 重启 VS Code,重现问题
- 在 VS Code 中,运行 TypeScript。关上 TS 服务器日志命令
- 这将关上 tsserver.log 文件
⚠正告:TSServer 日志可能会蕴含你的工作空间的信息,包含文件门路和源代码。如果你对在 GitHub 上公开公布有任何顾虑,请通知咱们,你能够私下分享细节。
The End