「這是我參與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: 以 / 活 ./ 或 ../ 開始的模塊。並不能解析 ambient module declaration。post
non-relative: "jquer",「@angular/core」等形式。能夠解析 ambient module declarationui
如上所述,解析策略有兩種。沒有特殊指定是,對於commonjs模塊,Node 是默認的策略;其餘模塊都是 Classic策略。能夠經過--moduleResolution 選項來指定策略。spa
過去是TS 的默認解析策略。如今是一種向下兼容的策略。命令行
對於 relative module :
在/root/src/folder/A.ts 文件中 import { b } from './moduleB' 的查找策略:
/root/src/folder/moduleB.ts
/root/src/folder/moduleB.d.ts
對於 non-relative module:
在/root/src/folder/A.ts 文件中 import { b } from 'moduleB' 的查找策略:
/root/src/folder/moduleB.ts
/root/src/folder/moduleB.d.ts
/root/src/moduleB.ts
/root/src/moduleB.d.ts
/root/moduleB.ts
/root/moduleB.d.ts
/moduleB.ts
/moduleB.d.ts
首先,瞭解一下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 往上查找
/root/src/node_modules/moduleB.js
/root/src/node_modules/moduleB/package.json
(if it specifies a "main"
property)
/root/src/node_modules/moduleB/index.js
/root/node_modules/moduleB.js
/root/node_modules/moduleB/package.json
(if it specifies a "main"
property)
/root/node_modules/moduleB/index.js
/node_modules/moduleB.js
/node_modules/moduleB/package.json
(if it specifies a "main"
property)
/node_modules/moduleB/index.js
如今瞭解一下 TS 是如何加載模塊的。
realtive module:文件查找順序 ts -> tsx -> .d.ts -> packages.json -> index.ts -> index.tsx -> index.d.ts。其中 package.json 中會查找 types 字段對應的值。
/root/src/moduleB.ts
/root/src/moduleB.tsx
/root/src/moduleB.d.ts
/root/src/moduleB/package.json
(if it specifies a "types"
property)/root/src/moduleB/index.ts
/root/src/moduleB/index.tsx
/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。
/root/src/node_modules/moduleB.ts
/root/src/node_modules/moduleB.tsx
/root/src/node_modules/moduleB.d.ts
/root/src/node_modules/moduleB/package.json
(if it specifies a "types"
property)
/root/src/node_modules/@types/moduleB.d.ts
/root/src/node_modules/moduleB/index.ts
/root/src/node_modules/moduleB/index.tsx
/root/src/node_modules/moduleB/index.d.ts
/root/node_modules/moduleB.ts
/root/node_modules/moduleB.tsx
/root/node_modules/moduleB.d.ts
/root/node_modules/moduleB/package.json
(if it specifies a "types"
property)
/root/node_modules/@types/moduleB.d.ts
/root/node_modules/moduleB/index.ts
/root/node_modules/moduleB/index.tsx
/root/node_modules/moduleB/index.d.ts
/node_modules/moduleB.ts
/node_modules/moduleB.tsx
/node_modules/moduleB.d.ts
/node_modules/moduleB/package.json
(if it specifies a "types"
property)
/node_modules/@types/moduleB.d.ts
/node_modules/moduleB/index.ts
/node_modules/moduleB/index.tsx
/node_modules/moduleB/index.d.ts
通知編譯器去哪裏尋找模塊。全部的 non-relative 模塊都會根據 baseUrl 讀取。而 relative module 不會受影響。
-> 命令行參數:若是值是相對路徑,那麼就是當前文件夾
-> tsconfig 配置:若是值是相對路徑,那麼就是 tsconfig 配置文件的相對路徑
能夠將模塊名映射到指定的文件/文件夾。
注意: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將他們「整合「到同一個虛擬的根目錄下面。
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「。
以下指令,能夠輸出 TS 解析模塊的順序
tsc --traceResolution
複製代碼
一般狀況下,編譯器在編譯前會解析全部導入的模塊。每次成功解析一個 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
複製代碼