TypeScript 模块解析

551 阅读3分钟

背景

最近我们在做小程序组件化,自己实现了小程序对NPM的支持(没有走微信小程序官方的npm)这样我们可以把业务组件和公共模块发布到公司的私有npm库来维护;

同时我们的小程序也在向TypeScript方向转型,公用模块在抽离为NPM包是,通过npm安装进小程序,引用是总是报“找不到模块”,即使我们有.d.ts类型声明模块,也在tsconfig.ts 中配置了 typeRoots 和 include 依赖无效;

后来翻阅文档,发现问题出在 moduleResolution 的配置上;

模块解析

当我们编写代码导入一个模块的时候,编译器需要知道怎么找到并解析我们导入的模块,会尝试定位表示导入模块的文件。 这里编译器会遵循以下二种策略之一: Classic或Node。 这些策略会告诉编译器到哪里去查找导入的模块。

import { getCurrentRoute } from '@toy/page-helper'

如上,如果我们尝试导入一个非相对路径的模块,而我们使用了错误的模块解析策略,会导致编译器不能解析这个模块,最终会报出一个 error TS2307: Cannot find module '@toy/page-helper' 的错误;

我们先来看下我们引入模块路径的的两种方式,这些方式编译器是怎么找找模块的;

相对路径

相对导入是以/,./或../开头的。 下面是一些例子:

import Entry from "./components/Entry";
import { DefaultHeaders } from "../constants/http";
import "/mod";

相对路径导入很简单,这里不做说明;

非相对路径

非相对路径导入的例子:

import * as CartApi from 'Apis/cart'
import Events from '@Libs/Events/index'
import { getCurrentRoute } from '@toy/page-helper'

模块解析策略

共有两种可用的模块解析策略:Node 和 Classic。

你可以使用 --moduleResolution标记来指定使用哪种模块解析策略。若未指定,那么在使用了 --module AMD | System | ES2015时的默认值为Classic,其它情况时则为Node。

Classic

相对路径导入模块:

//root/src/pages/index.ts
import { b } from "./page-helper"

Classic 策略下编译器的解析流程为:

* /root/src/pages/page-helper.ts
* /root/src/pages/page-helper.d.ts

会先找.ts文件,找不到在去找对应的.d.ts类型声明文件

非相对路径导入模块:

//root/src/folder/index.ts
import { b } from "page-helper"

Classic 策略下编译器的解析流程为:

* /root/src/pages/page-helper.ts
* /root/src/pages/page-helper.d.ts
* /root/src/page-helper.ts
* /root/src/page-helper.d.ts
* /root/page-helper.ts
* /root/page-helper.d.ts
* /page-helper.ts
* /page-helper.d.ts

我们最初没有指定 moduleResolution 配置,module 配置的是 “ES6”,此时 moduleResolution 的默认值是 Classic;

按照上边讲到的解析流程会发现,Classic 策略不会去解析 node_modules 中的模块无论是哪种路径;

Node

Node 策略下,TypeScript 的解析方式会和Node.js的模块解析机制类型,但细节会有不同。

差异来源 TypeScript 会多尝试解析 .tsx 和 .d.ts,同时TypeScript在 package.json里使用字段"types"来表示类似"main"的意义 - 编译器会使用它来找到要使用的"main"定义文件。

相对路径导入模块:

//root/src/pages/index.ts
import { b } from "./page-helper"

Node 策略下编译器的解析流程为:

1. /root/src/pages/page-helper.ts
2. /root/src/pages/page-helper.tsx
3. /root/src/pages/page-helper.d.ts
4. /root/src/pages/page-helper/package.json (如果指定了"types"属性)
5. /root/src/pages/page-helper/index.ts
6. /root/src/pages/page-helper/index.tsx
7. /root/src/pages/page-helper/index.d.ts

package.json 中如果指定了types 字段,则下一步会去找到types 字段指定的文件。

非相对路径导入模块:

//root/src/page/index.ts
import { b } from "page-helper"

Node 策略下编译器的解析流程为:

1. /root/src/pages/node_modules/page-helper.ts
2. /root/src/pages/node_modules/page-helper.tsx
3. /root/src/pages/node_modules/page-helper.d.ts
4. /root/src/pages/node_modules/page-helper/package.json 
5. /root/src/pages/node_modules/page-helper/index.ts
6. /root/src/pages/node_modules/page-helper/index.tsx
7. /root/src/pages/node_modules/page-helper/index.d.ts

如果pages目录下没有找到,则在去上一层目录中查找:

8. /root/src/node_modules/page-helper.ts
9. /root/src/node_modules/page-helper.tsx
10. /root/src/node_modules/page-helper.d.ts
11. /root/src/node_modules/page-helper/package.json 
12. /root/src/node_modules/page-helper/index.ts
13. /root/src/node_modules/page-helper/index.tsx
14. /root/src/node_modules/page-helper/index.d.ts

如果还未找到,则继续到上一层目录中的node_modules中查找,直到根路径下;

如此看,如果在TS中我们导入的是npm中的模块,我们期望是在node_modules中查找,则应该使用:

moduleResolution:Node

的配置策略。