模板的解析就是编译器通过导入语句如 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