关于前端:Typescript性能

80次阅读

共计 7892 个字符,预计需要花费 20 分钟才能阅读完成。

原文:https://github.com/microsoft/…

豆皮粉儿们,又又又见面了,明天这一期,由字节跳动数据平台的“StoneyAllen
”,给大家翻译一篇文章“typescript 性能”。在 2020 年倒计时最初几天我要祝大家新年快乐~,打工人,打工魂~。上面就开始仔细阅读吧!

翻译者:StoneyAllen

有些简略的 Typescript 配置,能够让你取得更快的编译和编辑体验,这些办法越早把握越好。上面列举了除了最佳实际以外,还有一些用于考察迟缓的编译 / 编辑体验的罕用技术,以及一些作为最初伎俩来帮忙 TypeScript 团队考察问题的罕用办法。

编写易编译代码

优先应用接口而不是穿插类型

很多时候,简略对象类型的类型别名与接口的作用十分类似

然而,只有你须要定义两个及以上的类型,你就能够选用接口来扩大这些类型,或者在类型别名中对它们相交,这时差别就变得显著了。

因为接口定义的是繁多立体对象类型,能够检测属性是否抵触,解决这些抵触是十分必要的。另一方面,穿插类型只是递归的合并属性,有些状况下会产生 never。接口则体现的一贯很好,而穿插类型定义的类型别名不能显示在其余的穿插类型上。接口之间的类型关系也会被缓存,而不是整个穿插类型。最初值得注意的区别是,如果是穿插类型,会在查看“无效”/“展平”类型之前查看所有属性。

因而,倡议在创立穿插类型时应用带有接口 / 扩大的扩大类型。

应用类型正文

增加类型正文,尤其是返回类型,能够节俭编译器的大量工作。这是因为命名类型比匿名类型更简洁(编译器更喜爱),这缩小了大量的读写申明文件的工夫。尽管类型推导是十分不便的,没有必要到处这么做。然而,如果您晓得了代码的慢速局部,可能会很有用。

优先应用根底类型而不是联结类型

联结类型十分好用 – 它能够让你表白一种类型的可能值范畴。

然而他们也带来了肯定开销。每次将参数传递给 printSchedule 时,须要比拟联结类型里的每个元素。对于一个由两个元素组成的联结类型来说,这是微不足道的。然而,如果你的联结类型有很多元素,这将引起编译速度的问题。例如,从联结类型中淘汰多余的局部,元素须要成对的去比拟,工作量是呈二次递增的。当大量联结类型穿插一起时产生这种查看,会在每个联结类型上相交导致大量的类型,须要缩小这种状况产生。防止这种状况的一种办法是应用子类型,而不是联结类型。

一个更事实的例子是,定义每种内置 DOM 元素的类型时。这种状况下,更优雅的形式是创立一个蕴含所有元素的 HtmlElement 根底类型,其中包含 DivElementImgElement 等。应用继承而不是创立一个无穷多的联结类型 DivElement | /*...*/ | ImgElement | /*...*/

应用我的项目援用

应用 TypeScript 构建内容较多的代码时,将代码库组织成几个独立的我的项目会很有用。每个我的项目都有本人的 tsconfig.json,可能它会对其余我的项目有依赖性。这有益于防止在一次编译中导入太多文件,也使某些代码库布局策略更容易地放在一起。

有一些十分根本的办法将一个代码库分解成多个我的项目。举个例子,一个程序代码,一部分用作客户端,一部分用作服务端,另一部分被其它两个共享

测试也能够合成到本人的我的项目中

一个常见的问题是 “ 一个我的项目应该有多大?”。这很像问 “ 一个函数应该有多大?” 或 “ 一个类应该有多大?”,在很大水平上,这归纳于教训。人们相熟的一种宰割 JS/TS 代码的办法是应用文件夹。作为一种启发式的办法,如果它们关联性足够大,能够放在同一个文件夹中,那么它们就属于同一个我的项目。除此之外,要避免出现极大或极小规模的我的项目。如果一个我的项目比其余所有我的项目加起来都要大,那就是一个正告信号。同样,最好防止有几十个单文件我的项目,因为也会减少开销。

你能够在这里浏览更多对于我的项目参考资料

配置 tsconfig.json 或 jsconfig.json

TypeScriptJavaScript 用户能够用 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 enumsnamespaces 这样的性能。然而须要查看其余文件来生成某个文件,这会使输入速度变慢。

对须要非本地信息的性能需要是比拟少见的 – 惯例枚举能够用来代替 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 日志

  1. 关上你的命令菜单栏,而后抉择

    1. 进入 “ 首选项 “ 关上您的全局设置。关上用户设置
    2. 入偏好设置,关上本地我的项目。关上工作区设置
  1. 设置选项 “typecript.tsserver.log”:”verbose”
  1. 重启 VS Code,重现问题
  1. 在 VS Code 中,运行 TypeScript。关上 TS 服务器日志命令
  1. 这将关上 tsserver.log 文件

⚠正告:TSServer 日志可能会蕴含你的工作空间的信息,包含文件门路和源代码。如果你对在 GitHub 上公开公布有任何顾虑,请通知咱们,你能够私下分享细节。

The End

正文完
 0