模板的解析就是編譯器通過導入語句如 import { a } from "moduleA"
找到 "moduleA" 模塊然后找到 a
的定義的過程。
moduleA
可能是在 .ts
或 .tsx
或 .d.ts
文件中。編譯器首先要做的就是找到對應的模塊文件。
- 首先編譯器通過
Classic
或Node
策略查找。 - 如果第一步查找不到,并且如果模塊名并不是相對的,則根據
ambient module
來查找。 - 如果以上方式都找不到,則拋出 查找 不到的異常。
Relative vs. No-relative imports
以 /
或 ./
或 ../
開頭的稱之為相對導入模塊名。其他的則稱為非相對導入。
相對導入是基于執行導入的當前文件的,而且不會以ambient module
的解析方式去解析。一般只用于內部模塊。
而非相對導入,一是可以基于 baseUrl
來查找 。或者根據 path
映射來查找 ,也會通過 ambient module
的聲明來查找 。可用于任何外部依賴。
模塊解析策略
Classic 策略
假設 /root/src/folder/A.ts
文件中有如下相對導入聲明 import { b } from "./moduleB"
。 那么將查找以下兩個路徑:
/root/src/folder/moduleB.ts
/root/src/folder/moduleB.d.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 策略
先來看一下 Node.js 中的導入邏輯,Node.js 的導入操作其實是由 require
函數完成
的。
假設 /root/src/moduleA.js
中有如下相對導入聲明: var x = require("./moduleB");
那么 Node.js 將按下面的順序解析:
/root/src/moduleB.js
- 是否有
/root/src/moduleB
目錄包含了一個package.json
的文件聲明了main
模塊,如果有如:{"main":"lib/mainModule.js"}
則解析/root/src/moduleB/lib/mainModule.js
- 是否有
/root/src/moduleB
目錄包含了名為index.js
的文件。
在 Node.js 中對于非相對導入的話,差別就大一些了。
還是按上面的假設。導入聲明換成非相對導入,即:var x = require("moduleB")
/root/src/node_modules/moduleB.js
/root/src/node_modules/moduleB/package.json
(如果有指定main
屬性)/root/src/node_modules/moduleB/index.js
/root/node_modules/moduleB.js
/root/node_modules/moduleB/package.json
(如果有指定main
屬性)/root/node_modules/moduleB/index.js
/node_modules/moduleB.js
/node_modules/moduleB/package.json
(如果有指定main
屬性)/node_modules/moduleB/index.js
TS 的模塊解析策略
TS 的相對導入
假設 /root/src/moduleA.ts
文件中有如下相對導入聲明 import { b } from "./moduleB"
。 那么將查找以下路徑:
/root/src/moduleB.ts
/root/src/moduleB.tsx
/root/src/moduleB.d.ts
-
/root/src/moduleB/package.json
(如果有指定types
屬性) /root/src/moduleB/index.ts
/root/src/moduleB/index.tsx
/root/src/moduleB/index.d.ts
TS 的非相對導入
假設 /root/src/moduleA.ts
文件中有如下相對導入聲明 import { b } from "moduleB"
。 那么將查找以下路徑:
/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/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/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/moduleB/index.ts
/node_modules/moduleB/index.tsx
/node_modules/moduleB/index.d.ts
TS 的模塊解析選項
Base URL
baseUrl
將告訴編譯器查找非相對導入起始路徑。
- 命令行參數指定, 如果是相當路徑則表示相對于當前路徑。
- 如果是通過
tsconfig.json
指定。則表示相對tsconfig.json
文件所在的路徑。
Path mapping
如果一些庫的路徑無法通過上面的標準解析路徑解析。那么可以通過在 tsconfig.json
中配置 paths
屬性。
如 jquery
庫的路徑是 node_modules/jquery/dist/jquery.slim.min.js
,不是一個標記的 node_modules 路徑。則可使用如下配置:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"jquery": ["node_modules/jquery/dist/jquery"]
}
}
}
值得注意的是 paths
是相對 baseUrl
的。
Virtual Directories with rootDirs
當項目源文件的結構是有多路徑,但是編譯結果會在一個目錄中。此時可以通過 rootDirs
來 指定編譯期其他的相對路徑查找列表。如下兩個 views
目錄下的文件就可以通過 ./template1.ts
來方式來引用另一目錄的模塊。
{
"compilerOptions": {
"rootDirs": [
"src/views",
"generated/templates/views"
]
}
}
Tracing module resolution
可以通過 tsc --traceResolution
來輸出模塊的解析日志。
參考 : Module Resolution