TypeScript基礎入門之模塊解析(一)

轉載 TypeScript基礎入門之模塊解析(一)node

模塊解析

本節假設有關模塊的一些基本知識。有關更多信息,請參閱模塊文檔。jquery

模塊解析是編譯器用來肯定導入所引用內容的過程。
考慮一個導入語句,如import { a } from "moduleA";
爲了檢查a的任何使用,編譯器須要確切地知道它表明什麼,而且須要檢查它的定義moduleA。算法

此時,編譯器將詢問"moduleA的類型是什麼?「雖然這聽起來很簡單,可是moduleA能夠在您本身的.ts/.tsx文件中定義,或者在您的代碼所依賴的.d.ts中定義。json

首先,編譯器將嘗試查找表示導入模塊的文件。
爲此,編譯器遵循兩種不一樣策略之一:Classic或Node。
這些策略告訴編譯器在哪裏尋找moduleA。函數

若是這不起做用而且模塊名稱是非相對的(而且在"moduleA"的狀況下,則是),則編譯器將嘗試查找環境模塊聲明。
接下來咱們將介紹非相對進口。佈局

最後,若是編譯器沒法解析模塊,它將記錄錯誤。
在這種狀況下,錯誤就像error TS2307: Cannot find module 'moduleA'ui

相對與非相對模塊導入

根據模塊引用是相對引用仍是非相對引用,模塊導入的解析方式不一樣。spa

相對導入是以/、./或../開頭的導入。
一些例子包括:component

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

任何其餘import都被視爲非親屬。
一些例子包括:blog

  • import * as $ from "jquery";
  • import { Component } from "@angular/core";

相對導入是相對於導入文件解析的,沒法解析爲環境模塊聲明。
您應該爲本身的模塊使用相對導入,這些模塊能夠保證在運行時保持其相對位置。

能夠相對於baseUrl或經過路徑映射解析非相對導入,咱們將在下面介紹。
他們還能夠解析爲環境模塊聲明。
導入任何外部依賴項時,請使用非相對路徑。

模塊解決策略

有兩種可能的模塊解析策略:Node和Classic。
您可使用--moduleResolution標誌指定模塊解析策略。
若是未指定,則默認爲Classic for ```--module AMD | System | ES2015```或其餘Node。

Classic 策略

這曾經是TypeScript的默認解析策略。
現在,這種策略主要用於向後兼容。

將相對於導入文件解析相對導入。
所以,從源文件/root/src/folder/A.ts中的import { b } from "./moduleB"將致使如下查找:

  • /root/src/folder/moduleB.ts
  • /root/src/folder/moduleB.d.ts

可是,對於非相對模塊導入,編譯器會從包含導入文件的目錄開始遍歷目錄樹,嘗試查找匹配的定義文件。

例如:

在源文件/root/src/folder/A.ts中對moduleB進行非相對導入(例如import { b } from "moduleB")將致使嘗試如下位置來定位"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 策略

他的解析策略試圖在運行時模仿Node.js模塊解析機制。Node.js模塊文檔中概述了完整的Node.js解析算法。

Node.js如何解析模塊

要了解TS編譯器將遵循的步驟,重要的是要闡明Node.js模塊。
傳統上,Node.js中的導入是經過調用名爲require的函數來執行的。
Node.js採起的行爲將根據require是否給定相對路徑或非相對路徑而有所不一樣。

相對路徑至關簡單。
例如,讓咱們考慮位於/root/src/moduleA.js的文件,其中包含import var x = require("./ moduleB");
Node.js按如下順序解析導入:

1. 詢問名爲/root/src/moduleB.js的文件(若是存在)。
2. 詢問文件夾/root/src/moduleB是否包含名爲package.json的文件,該文件指定了"main"模塊。在咱們的示例中,若是Node.js找到包含{"main": "lib/mainModule.js"}的文件/root/src/moduleB/package.json,那麼Node.js將引用/root/src/moduleB/lib/mainModule.js。
3. 詢問文件夾/root/src/moduleB是否包含名爲index.js的文件。該文件被隱含地視爲該文件夾的"main"模塊。

您能夠在Node.js文檔中瞭解有關文件模塊和文件夾模塊的更多信息。

可是,非相對模塊名稱的解析以不一樣方式執行。
Node將在名爲node_modules的特殊文件夾中查找模塊。
node_modules文件夾能夠與當前文件位於同一級別,或者在目錄鏈中位於更高級別。
Node將走向目錄鏈,查看每一個node_modules,直到找到您嘗試加載的模塊。

按照上面的示例,考慮是否/root/src/moduleA.js使用非相對路徑而且導入var x = require("moduleB");而後,Node會嘗試將moduleB解析到每一個位置,直到一個工做。

  • /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

請注意,Node.js在步驟(4)和(7)中跳過了一個目錄。

您能夠在Node.js文檔中閱讀有關從node_modules加載模塊的過程的更多信息。

TypeScript如何解析模塊

TypeScript將模仿Node.js運行時解析策略,以便在編譯時定位模塊的定義文件。
爲此,TypeScript經過Node的解析邏輯覆蓋TypeScript源文件擴展名(.ts、.tsx和.d.ts)。
TypeScript還將使用package.json中名爲"types"的字段來鏡像"main"的目的 - 編譯器將使用它來查找要查詢的"main"定義文件。

例如,/root/src/moduleA.ts中的import { b } from "./moduleB"等導入語句將致使嘗試如下位置來定位"./moduleB":

  • /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

回想一下,Node.js查找名爲moduleB.js的文件,而後查找適用的package.json,而後查找index.js。

相似地,非相對導入將遵循Node.js解析邏輯,首先查找文件,而後查找適用的文件夾。
所以,從源文件/root/src/moduleA.ts中的import { b } from "moduleB"將致使如下查找:

  • /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/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/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/moduleB/index.ts
  • /node_modules/moduleB/index.tsx
  • /node_modules/moduleB/index.d.ts

不要被這裏的步驟數嚇倒 - TypeScript仍然只在步驟(8)和(15)兩次跳過目錄。
這實際上並不比Node.js自己正在作的複雜。

附加模塊分辨率標誌

項目源佈局有時與輸出的佈局不匹配。
一般,一組構建步驟會致使生成最終輸出。
這些包括將.ts文件編譯爲.js,以及將不一樣源位置的依賴項複製到單個輸出位置。
最終結果是運行時的模塊可能具備與包含其定義的源文件不一樣的名稱。
或者,在編譯時,最終輸出中的模塊路徑可能與其對應的源文件路徑不匹配。

TypeScript編譯器具備一組附加標誌,用於通知編譯器預期發生在源上的轉換以生成最終輸出。

重要的是要注意編譯器不會執行任何這些轉換;
它只是使用這些信息來指導將模塊導入解析到其定義文件的過程。

未完待續...

相關文章
相關標籤/搜索