聊一聊 TypeScript 的工程引用

工程引用是 TypeScript 3.0 的新特性, 它支持將 TypeScript 程序的結構分割成更小的組成部分. 這樣能夠改善構建時間 (打開 composite 會自動開啓增量編譯), 強制在邏輯上對組件進行分離, 更好地組織你的代碼.node

不使用工程引用引起的痛點

考察下面的代碼結構: 假設這是一個先後端未分離的項目, client 目錄下存放的是客戶端代碼; server 目錄下存放的服務端代碼; common 存放的是一些共用代碼, 好比一些 util 方法, client/index.tsserver/index.ts 會引用這裏的代碼.git

__test__ 目錄則存放的是一些單元測試的代碼, 它會分別引用 src/client/index.tssrc/server/index.ts.github

而且整個工程只有一個 tsconfig.json, 它主要的配置是 "outDir": "./dist", 也就是將編譯後的文件存放到根目錄的 dist 文件夾.json

.
├── src
│   ├── client
│   │   ├── index.ts
│   ├── common
│   │   ├── index.ts
│   ├── server
│   │   ├── index.ts
├── __test__
│   ├── client.spec.ts
│   ├── server.spec.ts
├── package.json
├── tsconfig.json
└── yarn.lock
├── README.md
複製代碼

經過 tsc 命令進行編譯, dist 文件下大體以下.後端

.
├── dist
│   ├── src
│   │   ├── client
│   │   ├── common
│   │   ├── server
│   ├── __test__
複製代碼

經過上面的 code structure, 能夠羅列出幾個痛點:session

  • 咱們但願 src 下面的文件直接被編譯到 dist 目錄下, 但因爲 __test__ 的存在而達不到這樣的效果.單元測試

  • 咱們沒法單獨構建 client 端, 或者 server 端的代碼.測試

  • 咱們不但願把 __test__ 構建到 dist 目錄下.ui

使用工程引用改造上面的項目

經過上面的代碼, 咱們能感覺到僅僅一個 tsconfig.json 文件沒法靈活的 hold 住個性化的編譯配置, 所以嘗試給每一個目錄都增長一個 tsconfig.json. (爲了便於區分每一個 tsconfig.json 文件, 下面將使用 ①②③④⑤ 序號代替)es5

.
├── src
│   ├── client
│   │   ├── index.ts
│   │   ├── tsconfig.json // ①
│   ├── common
│   │   ├── index.ts
│   │   ├── tsconfig.json // ②
│   ├── server
│   │   ├── index.ts
│   │   ├── tsconfig.json // ③
├── __test__
│   ├── client.spec.ts
│   ├── server.spec.ts
│   ├── tsconfig.json // ④
├── package.json
├── tsconfig.json // ⑤
└── yarn.lock
├── README.md
複製代碼

接下來改造一下根 tsconfig.json 文件, 也就是 ⑤.

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "strict": true,
    // "outDir": "./dist"  關閉 outDir 選項, 即不在根配置中指定輸入目錄

    "composite": true, // 使用 composite, 它意味着工程能夠被引用, 並支持增量編譯
    "declaration": true // 使用 composite 選項必須開啓 declaration
  }
}
複製代碼

接下來改造 ①, 由於 ① 和 ③ 的配置大體相同, 這裏再也不贅述.

{
  "extends": "../../tsconfig.json", // 首先導入 ⑤
  "compilerOptions": {
    "outDir": "../../dist/client" // 指定輸入目錄
  },
  "references": [{ "path": "../common" }] // 由於 client 引用了 common, 故須要將 common 引入進來
}
複製代碼

對於 ②, 由於它沒有引用其餘的模塊, 所以只須要配置好 outDir 便可, 代碼以下:

{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "outDir": "../../dist/common"
  }
}
複製代碼

對於 ④, 爲了避免讓測試文件被編譯到 dist 目錄下, 就讓編譯後的文件也存放到 __test__ 好了.

{
  "extends": "../tsconfig.json",
  "references": [{ "path": "../src/client" }, { "path": "../src/server" }]
}
複製代碼

編譯

既然配置寫好了, 下面就能夠愉快的編譯了. 爲了支持工程引用, TypeScript 使用了 build 的構建模式, 它能夠構建單獨的工程, 相關依賴也能夠被自動構建, 下面咱們構建 server 端的應用, 其中 -b 是 build 的簡寫, verbose 能夠打印出一些構建信息.

tsc -b src/server --verbose
複製代碼

當 server 端被構建完畢後, server 文件夾和它所依賴的 common 文件夾就會被編譯到 dist 目錄下, 所以當你再編譯 client 時, common 就不用被重複編譯, 這樣就提高了編譯效率. 此外, 它會在每一個目錄下生成增量編譯文件, 這樣下次編譯時就很快了.

此外, 你能夠經過 --clean 參數來清除某個工程已構建的文件, 下面是清理 __test__ 產生的文件.

tsc -b __test__ --clean
複製代碼

總結

工程引用的優勢以下:

  • 解決了輸出目錄的結構問題

  • 解決了單個工程的構建問題

  • 經過增量編譯提升了編譯效率

其實 TypeScript 的官方源碼已經使用了工程引用技術, 在 src 目錄下有一個根 tsconfig-base.json, 在其餘的目錄下, 如server目錄下又有本身的 tsconfig.json, 摘錄以下:

{
  "extends": "../tsconfig-base",
  "compilerOptions": {
    "removeComments": false,
    "outFile": "../../built/local/server.js",
    "preserveConstEnums": true,
    "types": ["node"]
  },
  "references": [
    { "path": "../compiler" },
    { "path": "../jsTyping" },
    { "path": "../services" }
  ],
  "files": [
    "types.ts",
    "utilities.ts",
    "protocol.ts",
    "scriptInfo.ts",
    "typingsCache.ts",
    "project.ts",
    "editorServices.ts",
    "packageJsonCache.ts",
    "session.ts",
    "scriptVersionCache.ts"
  ]
}
複製代碼
相關文章
相關標籤/搜索