“这是我参与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
复制代码