鍍金池/ 教程/ HTML/ 模塊解析
初始化項(xiàng)目結(jié)構(gòu)
聯(lián)合類型
介紹
介紹
介紹
編譯選項(xiàng)
TypeScript 1.6
介紹
介紹
發(fā)展路線圖
介紹
在MSBuild里使用編譯選項(xiàng)
可迭代性
TypeScript 1.3
介紹
介紹
TypeScript 1.1
變量聲明
即將到來的Angular 2框架是使用TypeScript開發(fā)的。 因此Angular和TypeScript一起使用非常簡單方便
tsconfig.json
介紹
介紹
介紹
在MSBuild里使用編譯選項(xiàng)
使用TypeScript的每日構(gòu)建版本
新建工程
枚舉
三斜線指令
結(jié)合ASP.NET v5使用TypeScript
TypeScript里的this
介紹
TypeScript 1.4
編碼規(guī)范
介紹
模塊解析
ASP.NET 4
架構(gòu)概述
介紹
介紹
ASP.NET Core
TypeScript 1.8
介紹
介紹
創(chuàng)建簡單工程
TypeScript 1.7
TypeScript 1.5
NPM包的類型
支持TypeScript的編輯器

模塊解析

這節(jié)假設(shè)你已經(jīng)了解了模塊的一些基本知識 請閱讀模塊文檔了解更多信息。

模塊解析就是指編譯器所要依據(jù)的一個流程,用它來找出某個導(dǎo)入操作所引用的具體值。 假設(shè)有一個導(dǎo)入語句import { a } from "moduleA"; 為了去檢查任何對a的使用,編譯器需要準(zhǔn)確的知道它表示什么,并且會需要檢查它的定義moduleA。

這時(shí)候,編譯器會想知道“moduleA的shape是怎樣的?” 這聽上去很簡單,moduleA可能在你寫的某個.ts/.tsx文件里或者在你的代碼所依賴的.d.ts里。

首先,編譯器會嘗試定位表示導(dǎo)入模塊的文件。 編譯會遵循下列二種策略之一:ClassicNode。 這些策略會告訴編譯器到哪里去查找moduleA。

如果它們失敗了并且如果模塊名是非相對的(且是在"moduleA"的情況下),編譯器會嘗試定位一個外部模塊聲明。 我們接下來會講到非相對導(dǎo)入。

最后,如果編譯器還是不能解析這個模塊,它會記錄一個錯誤。 在這種情況下,錯誤可能為error TS2307: Cannot find module 'moduleA'.

相對 vs. 非相對模塊導(dǎo)入

根據(jù)模塊引用是相對的還是非相對的,模塊導(dǎo)入會以不同的方式解析。

相對導(dǎo)入是以/,./../開頭的。 下面是一些例子:

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

所有其它形式的導(dǎo)入被當(dāng)作非相對的。 下面是一些例子:

  • import * as $ from "jQuery";
  • import { Component } from "angular2/core";

相對導(dǎo)入解析時(shí)是相對于導(dǎo)入它的文件來的,并且不能解析為一個外部模塊聲明。 你應(yīng)該為你自己寫的模塊使用相對導(dǎo)入,這樣能確保它們在運(yùn)行時(shí)的相對位置。

模塊解析策略

共有兩種可用的模塊解析策略:NodeClassic。 你可以使用--moduleResolution標(biāo)記為指定使用哪個。 默認(rèn)值為Node。

Classic

這種策略以前是TypeScript默認(rèn)的解析策略。 現(xiàn)在,它存在的理由主要是為了向后兼容。

相對導(dǎo)入的模塊是相對于導(dǎo)入它的文件進(jìn)行解析的。 因此/root/src/folder/A.ts文件里的import { b } from "./moduleB"會使用下面的查找流程:

  1. /root/src/folder/moduleB.ts
  2. /root/src/folder/moduleB.d.ts

對了非相對模塊的導(dǎo)入,編譯器則會從包含導(dǎo)入文件的目錄開始依次向上級目錄遍歷,嘗試定位匹配的聲明文件。

比如:

有一個對moduleB的非相對導(dǎo)入import { b } from "moduleB",它是在/root/src/folder/A.ts文件里,會以如下的方式來定位"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

這個解析策略試圖在運(yùn)行時(shí)模仿Node.js模塊解析機(jī)制。 完整的Node.js解析算法可以在Node.js module documentation找到。

Node.js如何解析模塊

為了理解TypeScript編譯依照的解析步驟,先弄明白Node.js模塊是非常重要的。 通常,在Node.js里導(dǎo)入是通過require函數(shù)調(diào)用進(jìn)行的。 Node.js會根據(jù)require的是相對路徑還是非相對路徑做出不同的行為。

相對路徑很簡單。 例如,假設(shè)有一個文件路徑為/root/src/moduleA.js,包含了一個導(dǎo)入var x = require("./moduleB"); Node.js以下面的順序解析這個導(dǎo)入:

  1. /root/src/moduleB.js視為文件,檢查是否存在。

  2. /root/src/moduleB視為目錄,檢查是否它包含package.json文件并且其指定了一個"main"模塊。 在我們的例子里,如果Node.js發(fā)現(xiàn)文件/root/src/moduleB/package.json包含了{ "main": "lib/mainModule.js" },那么Node.js會引用/root/src/moduleB/lib/mainModule.js

  3. /root/src/moduleB視為目錄,檢查它是否包含index.js文件。 這個文件會被隱式地當(dāng)作那個文件夾下的"main"模塊。

你可以閱讀Node.js文檔了解更多詳細(xì)信息:file modulesfolder modules。

但是,非相對模塊名的解析是個完全不同的過程。 Node會在一個特殊的文件夾node_modules里查找你的模塊。 node_modules可能與當(dāng)前文件在同一級目錄下,或者在上層目錄里。 Node會向上級目錄遍歷,查找每個node_modules直到它找到要加載的模塊。

還是用上面例子,但假設(shè)/root/src/moduleA.js里使用的是非相對路徑導(dǎo)入var x = require("moduleB");。 Node則會以下面的順序去解析moduleB,直到有一個匹配上。

  1. /root/src/node_modules/moduleB.js
  2. /root/src/node_modules/moduleB/package.json (如果指定了"main"屬性)
  3. /root/src/node_modules/moduleB/index.js

  4. /root/node_modules/moduleB.js
  5. /root/node_modules/moduleB/package.json (如果指定了"main"屬性)
  6. /root/node_modules/moduleB/index.js

  7. /node_modules/moduleB.js
  8. /node_modules/moduleB/package.json (如果指定了"main"屬性)
  9. /node_modules/moduleB/index.js

注意Node.js在步驟(4)和(7)會向上跳一級目錄。

你可以閱讀Node.js文檔了解更多詳細(xì)信息:loading modules from node_modules。

TypeScript如何解析模塊

TypeScript是模仿Node.js運(yùn)行時(shí)的解析策略來在編譯階段定位模塊定義文件。 因此,TypeScript在Node解析邏輯基礎(chǔ)上增加了TypeScript源文件的擴(kuò)展名(.ts,.tsx.d.ts)。 同時(shí),TypeScript在package.json里使用字段"typings"來表示類似"main"的意義 - 編譯器會使用它來找到要使用的"main"定義文件。

比如,有一個導(dǎo)入語句import { b } from "./moduleB"/root/src/moduleA.ts里,會以下面的流程來定位"./moduleB"

  1. /root/src/moduleB.ts
  2. /root/src/moduleB.tsx
  3. /root/src/moduleB.d.ts
  4. /root/src/moduleB/package.json (如果指定了"typings"屬性)
  5. /root/src/moduleB/index.ts
  6. /root/src/moduleB/index.tsx
  7. /root/src/moduleB/index.d.ts

回想一下Node.js先查找moduleB.js文件,然后是合適的package.json,再之后是index.js。

類似地,非相對的導(dǎo)入會遵循Node.js的解析邏輯,首先查找文件,然后是合適的文件夾。 因此/src/moduleA.ts文件里的import { b } from "moduleB"會以下面的查找順序解析:

  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 (如果指定了"typings"屬性)
  5. /root/src/node_modules/moduleB/index.ts
  6. /root/src/node_modules/moduleB/index.tsx
  7. /root/src/node_modules/moduleB/index.d.ts

  8. /root/node_modules/moduleB.ts
  9. /root/node_modules/moduleB.tsx
  10. /root/node_modules/moduleB.d.ts
  11. /root/node_modules/moduleB/package.json (如果指定了"typings"屬性)
  12. /root/node_modules/moduleB/index.ts
  13. /root/node_modules/moduleB/index.tsx
  14. /root/node_modules/moduleB/index.d.ts

  15. /node_modules/moduleB.ts
  16. /node_modules/moduleB.tsx
  17. /node_modules/moduleB.d.ts
  18. /node_modules/moduleB/package.json (如果指定了"typings"屬性)
  19. /node_modules/moduleB/index.ts
  20. /node_modules/moduleB/index.tsx
  21. /node_modules/moduleB/index.d.ts

不要被這里步驟的數(shù)量嚇到 - TypeScript只是在步驟(8)和(15)向上跳了兩次目錄。 這并不比Node.js里的流程復(fù)雜。

使用--noResolve

正常來講編譯器會在開始編譯之前解析模塊導(dǎo)入。 每當(dāng)它成功地解析了對一個文件import,這個文件被會加到一個文件列表里,以供編譯器稍后處理。

--noResolve編譯選項(xiàng)告訴編譯器不要添加任何不是在命令行上傳入的文件到編譯列表。 編譯器仍然會嘗試解析模塊,但是只要沒有指定這個文件,那么它就不會被包含在內(nèi)。

比如

app.ts

import * as A from "moduleA" // OK, moduleA passed on the command-line
import * as B from "moduleB" // Error TS2307: Cannot find module 'moduleB'.
tsc app.ts moduleA.ts --noResolve

使用--noResolve編譯app.ts

  • 可能正確找到moduleA,因?yàn)樗诿钚猩现付恕?/li>
  • 找不到moduleB,因?yàn)闆]有在命令行上傳遞。

常見問題

為什么在exclude列表里的模塊還會被編譯器使用

tsconfig.json將文件夾轉(zhuǎn)變一個“工程” 如果不指定任何“exclude”“files”,文件夾里的所有文件包括tsconfig.json和所有的子目錄都會在編譯列表里。 如果你想利用“exclude”排除某些文件,甚至你想指定所有要編譯的文件列表,請使用“files”。

有些是被tsconfig.json自動加入的。 它不會涉及到上面討論的模塊解析。 如果編譯器識別出一個文件是模塊導(dǎo)入目標(biāo),它就會加到編譯列表里,不管它是否被排除了。

因此,要從編譯列表中排除一個文件,你需要在排除它的同時(shí),還要排除所有對它進(jìn)行import或使用了/// <reference path="..." />指令的文件。

上一篇:可迭代性下一篇:ASP.NET Core