王鹏飞

Blog

Tutorial

About

JavaScript

2021年9月23日

深入理解TypeScript的模块系统

在ES2015出现之前,JavaScript还没有原生的模块系统(ES Module),只能借助第三方的RequireJSSystemJS来使用模块功能。

模块本质上就是一个JS/TS文件,类似一个沙盒环境,使用import语句引入其他模块的API和值,export语句暴露自己的API和值。

TypeScript原生支持ES Module的语法,可以在.ts文件中使用importexport语句。默认情况下,TypeScript仅支持引入.ts.d.ts文件模块,可以通过修改allowJS配置选项,使TypeScirpt支持.js文件(这个在详解tsconfig.json文件文章中介绍过)。

一.导入模块的方式

// example.ts
import "../program";
import "package";

上面的代码,就是要介绍的两种导入模块的方式。

  • 路径:通过模块(文件)路径导入模块,可以是相对路径,也可以是绝对路径
  • 模块名称(包名):通过模块名称(包名称)导入模块。

第一种方式比较简单,typescript将会根据我们指定的文件路径寻找文件(program.ts或者program.d.ts),上面的例子中,typescript会在当前文件的父级目录中优先寻找program.ts,如果program.ts没有找到,再寻找program.d.ts,如果找到则导入找到的模块。

第二种方式,通过包名引入,情况比较复杂,接下来将重点介绍。

二.模块定位策略

上面的第二个例子import "package";,通过模块名导入模块。当typescirpt处理这种导入语句时,它会使用以下两个模块定位策略中的一个。

  • Classic模块定位策略:为了与旧版本兼容才发明的一种策略。
  • Node模块定位策略: 默认策略。

TypeScript的模块定位策略是可配置的,可以通过tsc--moduleResolution选项配置,也可以通过moduleResolution编译选项进行配置(tsc编译选项详解tsconfig.json文件详细讨论过)。

Classic模块定位策略

使用Classic模块定位策略,TypeScirpt首先在当前文件的同级目录查找与模块名相同的.ts文件或者.d.ts文件,如果没有找到,则向上一级目录查找,直到找到或者抵达磁盘根目录也未找到,如果找到则引入找到的模块。

以上面的例子import "package";为例。假设example.ts文件路径为D:\program\src\example.ts。首先在example.ts文件的同级目录D:\program\src\寻找package.tspackage.ds文件,如果没找到继续在父级目录D:\program\中寻找package.tspackage.ds文件,如果没找到继续向上在D盘根目录D:\中寻找,如果找到则引入模块。

Node模块定位策略

Node模块定位策略(Node Module Resolution)是TypeScript的默认定位策略,也是最常用的模块定位策略。

只要是JS程序员,肯定知道项目都有一个node_modules文件夹,里面存放了一些公共包,供项目使用。了解typeScript是如何利用Node模块定位策略的,先看一下Nodejs是如何定位模块的。

Node模块定位策略对于非Node.js程序员来说,可能有点复杂。与Classic模块定位策略相同,首先会向上遍历文件夹,寻找模块,但是也有一些不一样。

以下面的例子为例:

// D:\code\src\example.ts
import '../program';
import 'package';

第一个import语句,使用相对路径导入模块,这种情况下,首先在父级目录寻找program.tsprogram.d.ts文件,如果没有找到,则检查父级目录下有没有一个program文件夹,如果D:\code\src\program\存在,如果这个D:\code\src\program\目录下有package.json文件存在,然后它会导入package.jsontypestypings字段指定的路径的文件。

如果D:\code\src\program\目录下不存在package.json文件,或者它缺少types字段,然后它会检查该目录下是否有index.tsindex.d.ts文件,如果找到则导入模块,否则报错。

整个过程如下。

1. ../program.ts
2. ../program.d.ts
3. ../program/package.json -> [[types]] -> file path
4. ../program/index.ts
5. ../program/index.d.ts

6. Error: Cannot find module

对于第二个import语句,使用包名导入模块,情况变得更复杂了。首先,首先寻找当前目录下D:\code\srcnode_modules文件夹,然后按照与路径导入模块方式相同的方式查找模块。

如果它没有找到合适的文件,它会向上(父级目录,向外遍历)使用相同的逻辑寻找。整个过程如下。

Import statement: import 'package';
Import Location: D:\code\src\example.ts

1. D:/code/src/node_modules/package.ts
2. D:/code/src/node_modules/package.d.ts
3. D:/code/src/node_modules/package/package.json -> [[types]] ->
4. D:/code/src/node_modules/package/index.ts
5. D:/code/src/node_modules/package/index.d.ts

6. D:/code/node_modules/package.ts
7. D:/code/node_modules/package.d.ts
8. D:/code/node_modules/package/package.json -> [[types]] ->
9. D:/code/node_modules/package/index.ts
10. D:/code/node_modules/package/index.d.ts

11. D:/node_modules/package.ts
12. D:/node_modules/package.d.ts
13. D:/node_modules/package/package.json -> [[types]] ->
14. D:/node_modules/package/index.ts
15. D:/node_modules/package/index.d.ts

16. Error: Cannot find module

TypeScript利用Node模块定位策略

TypeScirpt利用Node模块定位策略导入模块时,当引用相对路径的模块时,是完全一样的,区别在于使用包名引入模块时,有一点区别,它会检查node_modules/@types文件夹,整个过程如下。

Import statement: import 'package';
Import Location: D:\code\src\example.ts

1. D:/code/src/node_modules/package.ts
2. D:/code/src/node_modules/package.d.ts
3. D:/code/src/node_modules/package/package.json -> [[types]] ->
4. D:/code/src/node_modules/@types/moduleB.d.ts
5. D:/code/src/node_modules/@types/moduleB/index.d.ts (或者package.json文件指定的入口文件)
6. D:/code/src/node_modules/package/index.ts
7. D:/code/src/node_modules/package/index.d.ts

8. D:/code/node_modules/package.ts
9. D:/code/node_modules/package.d.ts
10. D:/code/node_modules/package/package.json -> [[types]] ->
11. D:/code/node_modules/@types/moduleB.d.ts
12. D:/code/node_modules/@types/moduleB/index.d.ts (或者package.json文件指定的入口文件)
13. D:/code/node_modules/package/index.ts
14. D:/code/node_modules/package/index.d.ts

15. D:/node_modules/package.ts
16. D:/node_modules/package.d.ts
17. D:/node_modules/package/package.json -> [[types]] ->
18. D:/node_modules/@types/moduleB.d.ts
19. D:/node_modules/@types/moduleB/index.d.ts (或者package.json文件指定的入口文件)
20. D:/node_modules/package/index.ts
21. D:/node_modules/package/index.d.ts

22. Error: Cannot find module

附:参考资料

(完)

【TypeScirpt】详解tsconfig.json文件
【TypeScript】使用namespace封装数据

留言(0


发表评论

邮箱地址不会被公开。*表示必填项