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
复制代码
相关文章
相关标签/搜索