TypeScript 編譯性能優(yōu)化:Project Reference
TypeScript 給 JavaScript 添加了一套類型系統(tǒng),可以在編譯期間檢查出類型錯(cuò)誤,這增加了代碼的健壯性,但也多了一個(gè)編譯的過程。
ts 編譯速度與項(xiàng)目規(guī)模有關(guān),如果項(xiàng)目比較大,代碼很多,那就需要編譯很長一段時(shí)間。
有沒有什么辦法可以提升 tsc 編譯的性能呢?
還真有,TypeScript 3.0 的時(shí)候?qū)崿F(xiàn)了 Project Reference 的特性,就是用于優(yōu)化編譯和類型檢查的性能的。
那 Project Reference 是干什么的呢?
Project Reference
想象這樣一個(gè)場(chǎng)景。項(xiàng)目目錄下有兩個(gè)相對(duì)獨(dú)立的模塊 aaa 和 bbb。
它們是用同一個(gè) tsconfig.json 來配置編譯方式的:
執(zhí)行 tsc 就會(huì)編譯所有 include 的文件到 dist 目錄下:
假設(shè) aaa 和 bbb 都很大,編譯要很久,但是兩者的關(guān)聯(lián)還不是特別大。
aaa 模塊下的變動(dòng)基本和 bbb 模塊下的沒啥關(guān)系,但是 aaa 變了,bbb 也要重新編譯一遍,bbb 變了 aaa 也要重新編譯一遍,這就很沒必要。
有的同學(xué)說,那在 aaa 和 bbb 下分別放一個(gè) tsconfig.json,各自編譯各自的不就行了?
這樣是可以,但是這樣就是多次編譯了,比較麻煩。
能不能還是一次編譯,但是對(duì)一些相對(duì)獨(dú)立的模塊做下緩存,不要跟隨別的模塊一起編譯呢?
可以的,這就是 Project Reference 做的事情了。
在 aaa 和 bbb 下各自創(chuàng)建一個(gè) tsconfig.json,放各自的編譯配置。注意這里要加一個(gè) composite: true,這是 Project Reference 需要的:
然后在根目錄的 tsconfig.json 里加上一個(gè) references 的配置,引入 aaa 和 bbb:
這樣再執(zhí)行 tsc --build 進(jìn)行編譯,你會(huì)發(fā)現(xiàn)編譯結(jié)果多了 .d.ts 的聲明,還多了 tsconfig.tsbuildinfo 的文件:
打開 tsconfig.tsbuildinfo 看一下,會(huì)發(fā)現(xiàn)它記錄了編譯了哪些文件,還記錄了這些文件的 hash 值:
看到這里,你是不是就知道為啥它能實(shí)現(xiàn)緩存了?
沒錯(cuò),就是對(duì)比文件的 hash,當(dāng)編譯到這個(gè) project 的時(shí)候,會(huì)對(duì)比下 hash 有沒有變化,變了才去編譯。沒變的就直接跳過了。
而且,別的地方可能用到這個(gè) project 的類型,所以需要生成 d.ts 聲明文件,這就是為啥我們沒有指定 declaration: true 的配置,但是編譯產(chǎn)物里還是有 d.ts。其實(shí)這是 composite 選項(xiàng)做的,它設(shè)置了 Project Reference 需要的一些編譯選項(xiàng):
現(xiàn)在當(dāng)你修改了 aaa 下某個(gè)模塊的代碼,重新編譯的時(shí)候就不會(huì)編譯 bbb 了,只會(huì)編譯 aaa 下的那個(gè)模塊。
這就是 Project Reference 的作用。
當(dāng)然,這種編譯模式和常規(guī)的編譯模式不同,所以不是直接用 tsc 來編譯,而是用 tsc --build 或者 tsc -b。
此外,Project Reference 還支持通過 prepend 指定編譯順序,比如給 bbb 添加 prepend: true,那么就會(huì)先編譯 bbb,再編譯當(dāng)前項(xiàng)目,然后編譯 aaa:
其實(shí)很容易想到,monorepo 里就可以用 Project Reference 來提升 tsc 的編譯性能。因?yàn)?monorepo 下的多個(gè) project 相互之間都比較獨(dú)立,一個(gè)模塊的改動(dòng)一般不會(huì)影響另一個(gè)模塊,所以編譯的時(shí)候也應(yīng)該各自做緩存。
總結(jié)
TypeScript 3.0 時(shí)實(shí)現(xiàn)了 Project Reference 來優(yōu)化性能。
如果項(xiàng)目下有一些相對(duì)獨(dú)立的模塊,別的模塊的變動(dòng)不影響它,但是它卻要跟著重新編譯一次,這時(shí)就可以用 Project Reference 來優(yōu)化了。
在獨(dú)立的模塊下添加 tsconfig.json,加上 composite 的編譯選項(xiàng),在入口的 tsconfig.json 里配置 references 引用這些獨(dú)立的模塊。然后執(zhí)行 tsc --build 或者 tsc -b 來編譯。
這時(shí)候就實(shí)現(xiàn)了編譯和類型檢查的性能優(yōu)化。
原理是編譯時(shí)會(huì)生成 tsconfig.tsbuildinfo 的文件,記錄著編譯的文件和它們的 hash,當(dāng)再次編譯的時(shí)候,如果文件 hash 沒變,那就直接跳過,從而提升了編譯速度。
這是 TypeScript 提供的編譯性能優(yōu)化機(jī)制,當(dāng)項(xiàng)目比較大,tsc 執(zhí)行的速度比較慢的時(shí)候,不妨嘗試一下。