Typescript高性能

有些簡單的Typescript配置,可讓你得到更快的編譯和編輯體驗,這些方法越早掌握越好。下面列舉了除了最佳實踐之外,還有一些用於調查緩慢的編譯/編輯體驗的經常使用技術,以及一些做爲最後手段來幫助TypeScript團隊調查問題的經常使用方法。html

編寫易編譯代碼

優先使用接口而不是交叉類型

不少時候,簡單對象類型的類型別名與接口的做用很是類似node

image.png

然而,只要你須要定義兩個及以上的類型,你就能夠選用接口來擴展這些類型,或者在類型別名中對它們相交,這時差別就變得明顯了。webpack

因爲接口定義的是單一平面對象類型,能夠檢測屬性是否衝突,解決這些衝突是很是必要的。另外一方面,交叉類型只是遞歸的合併屬性,有些狀況下會產生never。接口則表現的一向很好,而交叉類型定義的類型別名不能顯示在其餘的交叉類型上。接口之間的類型關係也會被緩存,而不是整個交叉類型。最後值得注意的區別是,若是是交叉類型,會在檢查「有效」 /「展平」類型以前檢查全部屬性。git

所以,建議在建立交叉類型時使用帶有接口/擴展的擴展類型。web

image.png

使用類型註釋

添加類型註釋,尤爲是返回類型,能夠節省編譯器的大量工做。這是由於命名類型比匿名類型更簡潔(編譯器更喜歡),這減小了大量的讀寫聲明文件的時間。雖然類型推導是很是方便的,沒有必要處處這麼作。可是,若是您知道了代碼的慢速部分,可能會頗有用。typescript

image.png

優先使用基礎類型而不是聯合類型

聯合類型很是好用--它可讓你表達一種類型的可能值範圍。json

image.png

可是他們也帶來了必定開銷。每次將參數傳遞給 printSchedule 時,須要比較聯合類型裏的每一個元素。對於一個由兩個元素組成的聯合類型來講,這是微不足道的。可是,若是你的聯合類型有不少元素,這將引發編譯速度的問題。例如,從聯合類型中淘汰多餘的部分,元素須要成對的去比較,工做量是呈二次遞增的。當大量聯合類型交叉一塊兒時發生這種檢查,會在每一個聯合類型上相交致使大量的類型,須要減小這種狀況發生。避免這種狀況的一種方法是使用子類型,而不是聯合類型。gulp

image.png

一個更現實的例子是,定義每種內置DOM元素的類型時。這種狀況下,更優雅的方式是建立一個包含全部元素的 HtmlElement 基礎類型,其中包括 DivElementImgElement 等。使用繼承而不是建立一個無窮多的聯合類型 DivElement | /\*...\*/ | ImgElement | /\*...\*/緩存

使用項目引用

使用TypeScript構建內容較多的代碼時,將代碼庫組織成幾個獨立的項目會頗有用。每一個項目都有本身的 tsconfig.json ,可能它會對其餘項目有依賴性。這有益於避免在一次編譯中導入太多文件,也使某些代碼庫佈局策略更容易地放在一塊兒。安全

有一些很是基本的方法將一個代碼庫分解成多個項目。舉個例子,一個程序代碼,一部分用做客戶端,一部分用做服務端,另外一部分被其它兩個共享

image.png

測試也能夠分解到本身的項目中

image.png

一個常見的問題是 "一個項目應該有多大?"。這很像問 "一個函數應該有多大?"或 "一個類應該有多大?",在很大程度上,這歸結於經驗。人們熟悉的一種分割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,用來演示這個操做

image.png

控制包含的@types

默認狀況下,TypeScript會自動包含每個在node_modules文件夾中找到的@types包,無論你是否導入它。這是爲了在使用Node.js、Jasmine、Mocha、Chai等工具_包時,使某些東西 "可以工做",由於這些工具_包沒有被導入--它們只是被加載到全局環境中

有時這種邏輯在編譯和編輯場景下都會拖慢程序的構建時間,甚至會形成多個全局包的聲明衝突的問題,形成相似於以下問題

image.png

在不須要全局包的狀況下,修復方法很簡單,只要在 tsconfig.json/jsconfig.json 中爲 "type "選項指定一個空字段便可。

image.png

若是您仍然須要一些全局包,請將它們添加到類型字段中

image.png

增量項目輸出

--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會在早期告訴咱們這一點

image.png

記住: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,以得到編譯器花費時間的打印日誌。

image.png

請注意,總時間不是前面全部時間的總和,由於有一些重疊,有些工做是沒有衡量工具的。

對於大多數用戶來講,最相關的信息是:

考慮到這些投入,你可能會想問一些問題:

  • 文件數/代碼行數是否與您項目中的文件數大體一致?若是不符合,請嘗試運行--listFiles
  • 程序時間或I_O讀取時間是否至關高?請確保你的include_exclude配置正確
  • 其餘時間看起來不對勁嗎?你可能想提出一個問題。你能夠作如下事情來幫助診斷

    • 若是打印時間較高,則使用emitDeclarationOnly運行
    • 閱讀關於報告編譯器性能問題的說明

顯示配置

當運行 tsc 時,並不能明顯地看到編譯的內容設置,特別是考慮到 tsconfig.jsons 能夠擴展其餘配置文件。showConfig 能夠解釋 tsc 將爲一個調用計算着什麼。
image.png

追蹤分辨率

運行 traceResolution 能夠有助於解釋,一個文件爲何被包含在編譯中。輸出有點繁瑣,因此你可能想把輸出重定向到一個文件。

image.png

若是你發現了一個不該該存在的文件,你可能須要修改你的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版本,以確保你不會碰到那些已解決的問題。

image.png

一個編譯器的問題可能包括

  • 安裝的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+,這對團隊提供診斷結果來講是很重要的:

image.png

這裏的 ._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. 打開你的命令菜單欄,而後選擇

    • 進入 "首選項 "打開您的全局設置。打開用戶設置
    • 入偏好設置,打開本地項目。打開工做區設置
  2. 設置選項 "typecript.tsserver.log":"verbose"
  3. 重啓VS Code,重現問題
  4. 在VS Code中,運行TypeScript。打開TS服務器日誌命令
  5. 這將打開tsserver.log文件

⚠警告:TSServer日誌可能會包含你的工做空間的信息,包括文件路徑和源代碼。若是你對在GitHub上公開發布有任何顧慮,請告訴咱們,你能夠私下分享細節。

相關文章
相關標籤/搜索