欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

tsc性能優(yōu)化Project References使用詳解

 更新時間:2022年11月14日 09:08:44   作者:草帽Plasticine  
這篇文章主要為大家介紹了tsc性能優(yōu)化Project References使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

什么是 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新增的特性,這個特性有啥用呢?

將我們的項(xiàng)目分成多個小的片段,也就是允許我們將項(xiàng)目進(jìn)行分包模塊化

這樣一來我們在執(zhí)行tsc對項(xiàng)目進(jìn)行構(gòu)建的時候,無論是出于代碼轉(zhuǎn)譯成js的目的還是說出于單純的類型檢查(將compilerOptions.noEmit置為true)的目的

都可以嚴(yán)格按照自己的需要對想要被tsc處理的那部分代碼進(jìn)行處理,而不是每次都對整個項(xiàng)目進(jìn)行處理,這是我認(rèn)為Project References的最大好處

就以一個前后端代碼在同一個倉庫里維護(hù)的項(xiàng)目為例,如果沒有Project References,那么我執(zhí)行tsc時,會對前后端模塊都進(jìn)行類型檢查和轉(zhuǎn)譯

但實(shí)際上如果我只修改了前端部分的代碼,理所應(yīng)當(dāng)讓tsc只處理前端模塊,后端模塊的構(gòu)建產(chǎn)物不需要進(jìn)行重新構(gòu)建,有了Project References,我們就能實(shí)現(xiàn)到這個需求,從而優(yōu)化項(xiàng)目的構(gòu)建或類型檢查性能

相信大家對Project References有一個大概的認(rèn)識了,接下來我們就開始實(shí)際動手體驗(yàn)一下Project References加深我們對它的理解吧!

示例項(xiàng)目結(jié)構(gòu)

.
├── package.json
├── pnpm-lock.yaml
├── src
│   ├── __test__              // 單元測試
│   │   ├── client.test.ts    // 前端代碼測試
│   │   ├── index.ts          // 簡陋的單元測試 API
│   │   └── server.test.ts    // 后端代碼測試
│   ├── client                // 前端模塊
│   │   └── index.ts
│   ├── server                // 后端模塊
│   │   └── index.ts
│   └── shared                // 共享模塊 -- 包含通用的工具函數(shù)
│       └── index.ts
└── tsconfig.json             // TypeScript 配置

這是一個很常見的項(xiàng)目目錄結(jié)構(gòu),有前端代碼,有后端代碼,也有通用工具函數(shù)代碼以及前后端的單元測試代碼

它們的依賴關(guān)系如下:

  • client 依賴 shared
  • server 依賴 shared
  • __test__ 依賴 client 和 server
  • shared 無依賴

不使用 Project References 帶來的問題

現(xiàn)在整個項(xiàng)目只有一個tsconfig.json位于項(xiàng)目根目錄下,其內(nèi)容如下:

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

如果我們執(zhí)行tsc,它會將各個模塊的代碼都打包到項(xiàng)目根目錄的dist目錄下

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

這有一個很明顯的問題,正如前面所說,當(dāng)我們只修改一個模塊,比如只修改了前端模塊的代碼,那么理應(yīng)只需要再構(gòu)建前端模塊的產(chǎn)物即可,但是無論改動范圍如何,都是會將整個項(xiàng)目都構(gòu)建一次,這在項(xiàng)目規(guī)模變得越來越大的時候會帶來極大的性能問題,構(gòu)建時長會變得特別長

或許你會想著在每個模塊里創(chuàng)建一個tsconfig.json,然后通過tsc -p指定每個模塊的目錄去單獨(dú)對它們進(jìn)行構(gòu)建,沒錯,這是一種解決方案

但是這會帶來下面兩個問題:

  • 如果需要全量構(gòu)建項(xiàng)目,你得需要運(yùn)行三次tsc,對每個模塊分別構(gòu)建,而tsc的啟動時間開銷是比較大的,在這個小規(guī)模項(xiàng)目里甚至啟動開銷的時間比實(shí)際構(gòu)建的時間更長,現(xiàn)在還只是運(yùn)行三次tsc,如果項(xiàng)目模塊很多,有幾十上百個呢?那光是啟動tsc幾十上百次都已經(jīng)會花一些時間了
  • tsc -w不能一次監(jiān)控多個tsconfig.json,只能是對各個模塊都啟動一次tsc -w

Project References的出現(xiàn),就是為了解決上述問題的

tsconfig.json 的 references 配置項(xiàng)

Project References就是tsconfig.json里的references配置項(xiàng),其結(jié)構(gòu)是一個包含若干對象的數(shù)組,對象的結(jié)構(gòu)如下:

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

核心就是一個path屬性,該屬性指向被引用的項(xiàng)目模塊路徑,該路徑下需要包含tsconfig.json,如果該模塊不是用tsconfig.json命名的話,你也可以指定具體的文件名,比如:

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

當(dāng)指定了references選項(xiàng)后,會發(fā)生如下改變:

  • 在主模塊中導(dǎo)入被引用的模塊時,會加載它的類型聲明文件,也就是.d.ts后綴的文件
  • 使用tsc --buildtsc -b構(gòu)建主模塊時,會自動構(gòu)建被引用的模塊

這樣一來能夠帶來三個好處:

  • 提升類型檢查和構(gòu)建的速度
  • 減少IDE的運(yùn)行內(nèi)存占用
  • 更容易對項(xiàng)目結(jié)構(gòu)進(jìn)行劃分

tsconfig.json 的 composite 配置項(xiàng)

光是在主模塊中指定references配置項(xiàng)還不夠,還需要在被引用的項(xiàng)目對應(yīng)的tsconfig.json中開啟composite配置項(xiàng)

composite配置項(xiàng)又是干嘛的呢? -- 它可以幫助tsc快速確定如何尋找被引用項(xiàng)目的輸出產(chǎn)物

當(dāng)被引用的項(xiàng)目開啟composite配置項(xiàng)后,會有如下改變和要求:

當(dāng)未指定rootDir時,默認(rèn)值不再是The longest common path of all non-declaration input files,而是包含了tsconfig.json的目錄

Tips: 關(guān)于The longest common path of all non-declaration input files的意思可以到tsconfig.json 文章中關(guān)于 rootDir 的介紹中查閱

必須開啟include或者files配置項(xiàng)將要參與構(gòu)建的文件聲明進(jìn)來

必須開啟declaration配置項(xiàng)(因?yàn)榍懊娼榻Breferences的時候說了,會加載被引入模塊的類型聲明文件,因此被引用模塊自然得開啟declaration配置項(xiàng)生成自己的類型聲明文件供主模塊加載)

使用 Project References 改造示例項(xiàng)目

根據(jù)目前我們對Project References的認(rèn)識,現(xiàn)在可以開始改造一下我們的項(xiàng)目了,首先是根目錄下的tsconfig.json配置,它起到一個類似于項(xiàng)目入口的作用,因此這里面只負(fù)責(zé)添加references聲明項(xiàng)目中需要被構(gòu)建的模塊,以及通過exclude將不需要參與構(gòu)建的模塊排除(比如src/__test__中的測試代碼)

/tsconfig.json

{
  "references": [
    { "path": "src/client" },
    { "path": "src/server" },
    { "path": "src/shared" }
  ],
  "exclude": ["**/__test__"]
}

然后是各個子模塊的tsconfig.json配置,這里我們假設(shè)構(gòu)建目標(biāo)為es5的代碼,所以對于client、server以及shared來說是存在公共配置的,所以我們可以抽離出一個公共配置,然后在子模塊中通過extends配置項(xiàng)公用一個配置

/tsconfig.base.json

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

src/client/tsconfig.json

{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "../../dist/client",
    "composite": true,
    "declaration": true
  },
  // 依賴哪個模塊則引用哪個模塊
  "references": [{ "path": "../shared" }]
}

src/server/tsconfig.json

{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "../../dist/server",
    "composite": true,
    "declaration": true
  },
  // 依賴哪個模塊則引用哪個模塊
  "references": [{ "path": "../shared" }]
}

src/shared/tsconfig.json

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

全量構(gòu)建

現(xiàn)在我們在項(xiàng)目根目錄下運(yùn)行tsc --build --verbose,就會根據(jù)references配置去尋找各個子模塊,并對它們進(jìn)行構(gòu)建,可以理解為對項(xiàng)目的全量構(gòu)建

--build 參數(shù)表示讓tscbuild模式進(jìn)行構(gòu)建和類型檢查,也就是會使用references配置項(xiàng),如果不開啟的話是不會使用references配置項(xiàng)的,這點(diǎn)可以從官方文檔中得證:

--verbose 參數(shù)則是會將構(gòu)建過程中的輸出顯示在控制臺中,不開啟該參數(shù)的話則不會顯示輸出(除非構(gòu)建過程中報(bào)錯)

運(yùn)行后/dist目錄結(jié)構(gòu)如下

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

可以看到,所有子模塊都被構(gòu)建進(jìn)來了,各模塊的產(chǎn)物中有一個tsconfig.tsbuildinfo文件,這個文件起到一個類似緩存的作用,對于后續(xù)進(jìn)行增量構(gòu)建有著重要作用

前面看到的官方文檔中對tsc --build的作用的介紹中的第二點(diǎn)Detect if they are up-to-date主要就是依靠這個緩存文件去識別的

開啟--verbose參數(shù)后,可以看到控制臺輸出如下:

[4:50:26 PM] Projects in this build:
    * src/shared/tsconfig.json
    * src/client/tsconfig.json
    * src/server/tsconfig.json
    * tsconfig.json
[4:50:26 PM] Project '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/src/shared/tsconfig.json'...
[4:50:28 PM] Project '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/src/client/tsconfig.json'...
[4:50:28 PM] Project '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/src/server/tsconfig.json'...
[4:50:29 PM] Project 'tsconfig.json' is out of date because output 'src/shared/index.js' is older than input '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意思就是這個模塊沒被構(gòu)建過,因此會開始對其進(jìn)行構(gòu)建

由于我們是首次構(gòu)建,所以三個模塊都是沒被構(gòu)建過的,所以三個模塊都被檢測為out of date

當(dāng)我們再次運(yùn)行tsc --build --verbose時,輸出如下:

4:54:35 PM - Projects in this build:
    * src/shared/tsconfig.json
    * src/client/tsconfig.json
    * src/server/tsconfig.json
    * tsconfig.json
4:54:35 PM - Project 'src/shared/tsconfig.json' is up to date because newest input 'src/shared/index.ts' is older than output 'dist/shared/tsconfig.tsbuildinfo'
4:54:35 PM - Project 'src/client/tsconfig.json' is up to date because newest input 'src/client/index.ts' is older than output 'dist/client/tsconfig.tsbuildinfo'
4:54:35 PM - Project 'src/server/tsconfig.json' is up to date because newest input '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 'src/client/index.js'

可以看到,所有模塊都被檢測為up to date,從而避免了重復(fù)構(gòu)建

增量構(gòu)建

如果現(xiàn)在我們修改了client模塊的代碼,再運(yùn)行tsc --build --verbose會怎樣呢?估計(jì)你也能猜到了,只有client模塊會被構(gòu)建,而其他模塊則會跳過

4:56:44 PM - Projects in this build:
    * src/shared/tsconfig.json
    * src/client/tsconfig.json
    * src/server/tsconfig.json
    * tsconfig.json
4:56:44 PM - Project 'src/shared/tsconfig.json' is up to date because newest input 'src/shared/index.ts' is older than output 'dist/shared/tsconfig.tsbuildinfo'
4:56:44 PM - Project 'src/client/tsconfig.json' is out of date because output 'dist/client/tsconfig.tsbuildinfo' is older than input 'src/client/index.ts'
4:56:44 PM - Building project '/home/plasticine/demo/ts-reference-demo/src/client/tsconfig.json'...
4:56:45 PM - Project 'src/server/tsconfig.json' is up to date because newest input '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 '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'...

相信現(xiàn)在你能體會到Project References的好處了吧,能夠很大程度上優(yōu)化我們的構(gòu)建速度!

不過實(shí)際開發(fā)中,tsc更多的是用來進(jìn)行類型檢查,至于compile的工作,則更多地是交給如Babelswc、esbuild等工具去完成,這也是官方文檔中有提到過的

這也是為什么你在vite創(chuàng)建的項(xiàng)目中能夠看到默認(rèn)的build命令配置為tsc && vite build,正是將類型檢查的工作交給tsc,而構(gòu)建工作則交給vite底層依賴的rollup去完成

對__test__測試代碼的處理

我們的改造貌似已經(jīng)完成了,但其實(shí)還忽略了一個src/__test__,它也可以被視為一個模塊,它作為主模塊,依賴了clientserver,因此也可以給它加上tsconfig.json配置,并且對于測試代碼,我們一般不希望將它們構(gòu)建成js,只希望tsc負(fù)責(zé)類型檢查的工作,因此我們需要進(jìn)行如下配置:

src/__test__/tsconfig.json

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

noEmit的作用剛剛在官方文檔中也看到了,不會把產(chǎn)物文件輸出,如果我們只需要類型檢查能力的話很適合開啟該配置項(xiàng)

現(xiàn)在我們?nèi)绻枰獙?code>__test__中的代碼進(jìn)行類型檢查的話,只需要執(zhí)行:

# 忽略 references 配置項(xiàng)
tsc --project src/__test__
# 啟用 references 配置項(xiàng)
tsc --build src/__test__

如果是使用--project參數(shù)的話,tsconfig.json中可以忽略references配置項(xiàng),因?yàn)榧幢闩渲昧艘膊粫皇褂茫@在依賴產(chǎn)物未構(gòu)建出來時能起作用

而如果使用--build參數(shù),并且clientserver未構(gòu)建出來時,會先構(gòu)建它們,再對測試代碼進(jìn)行類型檢查,可以根據(jù)個人需求場景來決定使用--project還是--build

總結(jié)

本篇文章介紹了Project References是什么,并通過一個簡單的示例項(xiàng)目,并結(jié)合TypeScript Documentation官方文檔邊實(shí)戰(zhàn)邊解釋

總的來說,其使用起來就是:

  • 主模塊(tsc --build作用的模塊視為主模塊)中通過references配置項(xiàng)聲明依賴的模塊
  • 被引用模塊中開啟compositedeclaration配置項(xiàng)以支持被引用
  • 通過tsc --build 主模塊才可以啟用references配置項(xiàng),這在官方文檔中被稱為Build Mode,如果直接tsc 主模塊的話,是不會啟用references配置項(xiàng)的,也就導(dǎo)致依然會對項(xiàng)目中的所有ts文件進(jìn)行編譯(如果沒配置includefiles配置項(xiàng)的話)

希望通過本篇文章,能夠讓你對Project References有一個全面了解,也希望能夠?qū)⑵溆迷谀愕捻?xiàng)目中,提升類型檢查或構(gòu)建(使用 tsc 進(jìn)行構(gòu)建的話)的速度,更多關(guān)于tsc性能Project References的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論