TypeScript學習(十六):模塊解析 | 八月更文挑戰

這是我參與8月更文挑戰的第2天,活動詳情查看:8月更文挑戰node

模塊解析是編譯器找出導入內容所指向地方的過程。以 import { a } from "moduleA" 爲例,爲例檢查對象 a 的使用狀況,編譯器必須知道 a 的含義,而且須要檢查 moduleA。jquery

此時,編譯器會問 「moduleA」 的類型是什麼?固然,這個問題聽起來彷佛很簡單,moduleA 在ts/tsx 文件中被定義,或者依賴於 .d.ts 文件。json

首先,編譯器會嘗試定義一個能夠表明被導入模塊的文件。編譯器會在以下兩種策略中選擇一種去定位文件:Classic 、Node。這兩種策略會告訴編譯器在哪裏尋找 moduleA。數組

若是這兩種都不起做用而且若是模塊名稱 non-relative ,編譯器會嘗試定位 ambient module declaration。markdown

最終,若是不能解析模塊,會打印錯誤。app

Relative vs. Non-relative module imports

Relative: 以 / 活 ./ 或 ../ 開始的模塊。並不能解析 ambient module declaration。post

non-relative: "jquer",「@angular/core」等形式。能夠解析 ambient module declarationui

模塊解析策略

如上所述,解析策略有兩種。沒有特殊指定是,對於commonjs模塊,Node 是默認的策略;其餘模塊都是 Classic策略。能夠經過--moduleResolution 選項來指定策略。spa

Classic

過去是TS 的默認解析策略。如今是一種向下兼容的策略。命令行

對於 relative module :

在/root/src/folder/A.ts 文件中 import { b } from './moduleB' 的查找策略:

  1. /root/src/folder/moduleB.ts

  2. /root/src/folder/moduleB.d.ts

對於 non-relative module:

在/root/src/folder/A.ts 文件中 import { b } from 'moduleB' 的查找策略:

  1. /root/src/folder/moduleB.ts

  2. /root/src/folder/moduleB.d.ts

  3. /root/src/moduleB.ts

  4. /root/src/moduleB.d.ts

  5. /root/moduleB.ts

  6. /root/moduleB.d.ts

  7. /moduleB.ts

  8. /moduleB.d.ts

Node

首先,瞭解一下node是如何解析模塊的。

relative module:var x = require("./moduleB")

1. /root/src/moduleB.js 是否存在

2. 文件夾 /root/src/moduleB 是否存在package.json,若是有就讀取其中的 main 字段所指向的文件

3. 文件夾 /root/src/moduleB 中是否存在 index.js

non-relative module:var x = require("moduleB")

遞歸地從當前文件夾的 node_modules 往上查找

  1. /root/src/node_modules/moduleB.js

  2. /root/src/node_modules/moduleB/package.json (if it specifies a "main" property)

  3. /root/src/node_modules/moduleB/index.js

  4. /root/node_modules/moduleB.js

  5. /root/node_modules/moduleB/package.json (if it specifies a "main" property)

  6. /root/node_modules/moduleB/index.js

  7. /node_modules/moduleB.js

  8. /node_modules/moduleB/package.json (if it specifies a "main" property)

  9. /node_modules/moduleB/index.js

如今瞭解一下 TS 是如何加載模塊的。

realtive module:文件查找順序 ts -> tsx -> .d.ts -> packages.json -> index.ts -> index.tsx -> index.d.ts。其中 package.json 中會查找 types 字段對應的值。

  1. /root/src/moduleB.ts
  2. /root/src/moduleB.tsx
  3. /root/src/moduleB.d.ts
  4. /root/src/moduleB/package.json (if it specifies a "types" property)
  5. /root/src/moduleB/index.ts
  6. /root/src/moduleB/index.tsx
  7. /root/src/moduleB/index.d.ts

non-relative module:

與 node 相同,也是遞歸查找。不過多了tsx文件 與 @types 文件夾查找。

其中 ts -> tsx -> d.ts -> packages.json ( types ) -> @types/ .d.ts -> index.ts -> index.tsx -> index.d.ts。

  1. /root/src/node_modules/moduleB.ts

  2. /root/src/node_modules/moduleB.tsx

  3. /root/src/node_modules/moduleB.d.ts

  4. /root/src/node_modules/moduleB/package.json (if it specifies a "types" property)

  5. /root/src/node_modules/@types/moduleB.d.ts

  6. /root/src/node_modules/moduleB/index.ts

  7. /root/src/node_modules/moduleB/index.tsx

  8. /root/src/node_modules/moduleB/index.d.ts

  9. /root/node_modules/moduleB.ts

  10. /root/node_modules/moduleB.tsx

  11. /root/node_modules/moduleB.d.ts

  12. /root/node_modules/moduleB/package.json (if it specifies a "types" property)

  13. /root/node_modules/@types/moduleB.d.ts

  14. /root/node_modules/moduleB/index.ts

  15. /root/node_modules/moduleB/index.tsx

  16. /root/node_modules/moduleB/index.d.ts

  17. /node_modules/moduleB.ts

  18. /node_modules/moduleB.tsx

  19. /node_modules/moduleB.d.ts

  20. /node_modules/moduleB/package.json (if it specifies a "types" property)

  21. /node_modules/@types/moduleB.d.ts

  22. /node_modules/moduleB/index.ts

  23. /node_modules/moduleB/index.tsx

  24. /node_modules/moduleB/index.d.ts

額外的模塊解析參數 Additional module resolution flags

Base URL

通知編譯器去哪裏尋找模塊。全部的 non-relative 模塊都會根據 baseUrl 讀取。而 relative module 不會受影響。

-> 命令行參數:若是值是相對路徑,那麼就是當前文件夾

-> tsconfig 配置:若是值是相對路徑,那麼就是 tsconfig 配置文件的相對路徑

Path mapping

能夠將模塊名映射到指定的文件/文件夾。

注意:paths 的值是相對於 baseUrl 的。因此若是指定了 path,baseUrl 也應該指定。

{
    "compilerOptions":{
        "baseUrl": ".",
        "paths": {
            "jquery": ["node_modules/jquery/dist/jquery"]
        }
    }
}
複製代碼

paths 值也能夠是一個數組,做爲查找時候的降級方案。

projectRoot
├── folder1
│   ├── file1.ts (imports 'folder1/file2' and 'folder2/file3')
│   └── file2.ts
├── generated
│   ├── folder1
│   └── folder2
│       └── file3.ts
└── tsconfig.json



{  "compilerOptions": {    "baseUrl": ".",    "paths": {      "*": ["*", "generated/*"]    }  }}
複製代碼

虛擬目錄 rootDirs

有時候會從多目錄中讀取文件,最終將他們編譯成一份文件。這時候就可使用rootDirs將他們「整合「到同一個虛擬的根目錄下面。

src
 └── views
     └── view1.ts (imports './template1')
     └── view2.ts

 generated
 └── templates
         └── views
             └── template1.ts (imports './view2')

{  "compilerOptions": {    "rootDirs": ["src/views", "generated/templates/views"]  }}
複製代碼

除此以外,rootDirs 有更強大的功能。

好比在國際化的時候,須要打包成不一樣的語言。引用路徑中包含特殊字符 ./#{locale}/messages
在開發的時候能夠進行以下配置

{  "compilerOptions": {    "rootDirs": ["src/zh", "src/de", "src/#{locale}"]  }}
複製代碼

在導入的時候,會把 "src/#{locale}" -> 解析成 」src/zh「。

Tracing module resolution

以下指令,能夠輸出 TS 解析模塊的順序

tsc --traceResolution
複製代碼

使用 --noResolve

一般狀況下,編譯器在編譯前會解析全部導入的模塊。每次成功解析一個 import ,被解析的文件就會被添加到集合內部。稍後進行處理。
-- noResolve 編譯選項讓編譯器不要 」添加「 任何沒在命令行指定的模塊文件。

app.ts

import * as A from "moduleA"; // OK, 'moduleA' passed on the command-lineimport * as B from "moduleB"; // Error TS2307: Cannot find module 'moduleB'.

tsc app.ts moduleA.ts --noResolve
複製代碼
相關文章
相關標籤/搜索