TypeScript——模塊解析(2)

路徑映射

有時模塊不是直接放在baseUrl下面兽肤。 比如,充分 "jquery"模塊地導(dǎo)入电禀,在運行時可能被解釋為"node_modules/jquery/dist/jquery.slim.min.js"笤休。 加載器使用映射配置來將模塊名映射到運行時的文件,查看 RequireJs documentation和SystemJS documentation葫松。

TypeScript編譯器通過使用tsconfig.json文件里的"paths"來支持這樣的聲明映射腋么。 下面是一個如何指定 jquery的"paths"的例子亥揖。

{

? "compilerOptions": {

? ? "baseUrl": ".", // This must be specified if "paths" is.

? ? "paths": {

? ? ? "jquery": ["node_modules/jquery/dist/jquery"] // 此處映射是相對于"baseUrl"

? ? }

? }

}

請注意"paths"是相對于"baseUrl"進行解析费变。 如果 "baseUrl"被設(shè)置成了除"."外的其它值,比如tsconfig.json所在的目錄扛稽,那么映射必須要做相應(yīng)的改變在张。 如果你在上例中設(shè)置了 "baseUrl": "./src"矮慕,那么jquery應(yīng)該映射到"../node_modules/jquery/dist/jquery"。

通過"paths"我們還可以指定復(fù)雜的映射瘟斜,包括指定多個回退位置螺句。 假設(shè)在一個工程配置里橡类,有一些模塊位于一處猫态,而其它的則在另個的位置。 構(gòu)建過程會將它們集中至一處勇凭。 工程結(jié)構(gòu)可能如下:

projectRoot

├── folder1

│? ├── file1.ts (imports 'folder1/file2' and 'folder2/file3')

│? └── file2.ts

├── generated

│? ├── folder1

│? └── folder2

│? ? ? └── file3.ts

└── tsconfig.json

相應(yīng)的tsconfig.json文件如下:

{

? "compilerOptions": {

? ? "baseUrl": ".",

? ? "paths": {

? ? ? "*": [

? ? ? ? "*",

? ? ? ? "generated/*"

? ? ? ]

? ? }

? }

}

它告訴編譯器所有匹配"*"(所有的值)模式的模塊導(dǎo)入會在以下兩個位置查找:

"*": 表示名字不發(fā)生改變虾标,所以映射為<moduleName> => <baseUrl>/<moduleName>

"generated/*"表示模塊名添加了“generated”前綴璧函,所以映射為<moduleName> => <baseUrl>/generated/<moduleName>

按照這個邏輯基显,編譯器將會如下嘗試解析這兩個導(dǎo)入:

導(dǎo)入'folder1/file2'

匹配'*'模式且通配符捕獲到整個名字。

嘗試列表里的第一個替換:'*' -> folder1/file2库继。

替換結(jié)果為非相對名 - 與baseUrl合并 -> projectRoot/folder1/file2.ts宪萄。

文件存在榨惰。完成琅催。

導(dǎo)入'folder2/file3'

匹配'*'模式且通配符捕獲到整個名字。

嘗試列表里的第一個替換:'*' -> folder2/file3排监。

替換結(jié)果為非相對名 - 與baseUrl合并 -> projectRoot/folder2/file3.ts舆床。

文件不存在嫁佳,跳到第二個替換挨队。

第二個替換:'generated/*' -> generated/folder2/file3。

替換結(jié)果為非相對名 - 與baseUrl合并 -> projectRoot/generated/folder2/file3.ts蒿往。

文件存在盛垦。完成。

利用rootDirs指定虛擬目錄

有時多個目錄下的工程源文件在編譯時會進行合并放在某個輸出目錄下瓤漏。 這可以看做一些源目錄創(chuàng)建了一個“虛擬”目錄腾夯。

利用rootDirs颊埃,可以告訴編譯器生成這個虛擬目錄的roots; 因此編譯器可以在“虛擬”目錄下解析相對模塊導(dǎo)入蝶俱,就 好像它們被合并在了一起一樣班利。

比如榨呆,有下面的工程結(jié)構(gòu):

src

└── views

? ? └── view1.ts (imports './template1')

? ? └── view2.ts

generated

└── templates

? ? ? ? └── views

? ? ? ? ? ? └── template1.ts (imports './view2')

src/views里的文件是用于控制UI的用戶代碼罗标。 generated/templates是UI模版,在構(gòu)建時通過模版生成器自動生成积蜻。 構(gòu)建中的一步會將 /src/views和/generated/templates/views的輸出拷貝到同一個目錄下闯割。 在運行時,視圖可以假設(shè)它的模版與它同在一個目錄下竿拆,因此可以使用相對導(dǎo)入 "./template"宙拉。

可以使用"rootDirs"來告訴編譯器。 "rootDirs"指定了一個roots列表如输,列表里的內(nèi)容會在運行時被合并鼓黔。 因此,針對這個例子不见, tsconfig.json如下:

{

? "compilerOptions": {

? ? "rootDirs": [

? ? ? "src/views",

? ? ? "generated/templates/views"

? ? ]

? }

}

每當編譯器在某一rootDirs的子目錄下發(fā)現(xiàn)了相對模塊導(dǎo)入澳化,它就會嘗試從每一個rootDirs中導(dǎo)入。

rootDirs的靈活性不僅僅局限于其指定了要在邏輯上合并的物理目錄列表稳吮。它提供的數(shù)組可以包含任意數(shù)量的任何名字的目錄缎谷,不論它們是否存在。這允許編譯器以類型安全的方式處理復(fù)雜捆綁(bundles)和運行時的特性灶似,比如條件引入和工程特定的加載器插件列林。

設(shè)想這樣一個國際化的場景,構(gòu)建工具自動插入特定的路徑記號來生成針對不同區(qū)域的捆綁酪惭,比如將#{locale}做為相對模塊路徑./#{locale}/messages的一部分希痴。在這個假定的設(shè)置下,工具會枚舉支持的區(qū)域春感,將抽像的路徑映射成./zh/messages砌创,./de/messages等。

假設(shè)每個模塊都會導(dǎo)出一個字符串的數(shù)組鲫懒。比如./zh/messages可能包含:

export default [

? ? "您好嗎",

? ? "很高興認識你"

];

利用rootDirs我們可以讓編譯器了解這個映射關(guān)系嫩实,從而也允許編譯器能夠安全地解析./#{locale}/messages,就算這個目錄永遠都不存在窥岩。比如甲献,使用下面的tsconfig.json:

{

? "compilerOptions": {

? ? "rootDirs": [

? ? ? "src/zh",

? ? ? "src/de",

? ? ? "src/#{locale}"

? ? ]

? }

}

編譯器現(xiàn)在可以將import messages from './#{locale}/messages'解析為import messages from './zh/messages'用做工具支持的目的,并允許在開發(fā)時不必了解區(qū)域信息颂翼。

跟蹤模塊解析

如之前討論晃洒,編譯器在解析模塊時可能訪問當前文件夾外的文件慨灭。 這會導(dǎo)致很難診斷模塊為什么沒有被解析,或解析到了錯誤的位置球及。 通過 --traceResolution啟用編譯器的模塊解析跟蹤缘挑,它會告訴我們在模塊解析過程中發(fā)生了什么。

假設(shè)我們有一個使用了typescript模塊的簡單應(yīng)用桶略。 app.ts里有一個這樣的導(dǎo)入import * as ts from "typescript"。

│? tsconfig.json

├───node_modules

│? └───typescript

│? ? ? └───lib

│? ? ? ? ? ? ? typescript.d.ts

└───src

? ? ? ? app.ts

使用--traceResolution調(diào)用編譯器诲宇。

tsc --traceResolution

輸出結(jié)果如下:

======== Resolving module 'typescript' from 'src/app.ts'. ========

Module resolution kind is not specified, using 'NodeJs'.

Loading module 'typescript' from 'node_modules' folder.

File 'src/node_modules/typescript.ts' does not exist.

File 'src/node_modules/typescript.tsx' does not exist.

File 'src/node_modules/typescript.d.ts' does not exist.

File 'src/node_modules/typescript/package.json' does not exist.

File 'node_modules/typescript.ts' does not exist.

File 'node_modules/typescript.tsx' does not exist.

File 'node_modules/typescript.d.ts' does not exist.

Found 'package.json' at 'node_modules/typescript/package.json'.

'package.json' has 'types' field './lib/typescript.d.ts' that references 'node_modules/typescript/lib/typescript.d.ts'.

File 'node_modules/typescript/lib/typescript.d.ts' exist - use it as a module resolution result.

======== Module name 'typescript' was successfully resolved to 'node_modules/typescript/lib/typescript.d.ts'. ========

需要留意的地方

導(dǎo)入的名字及位置

======== Resolving module 'typescript' from 'src/app.ts'. ========

編譯器使用的策略

Module resolution kind is not specified, using 'NodeJs'.

從npm加載types

'package.json' has 'types' field './lib/typescript.d.ts' that references 'node_modules/typescript/lib/typescript.d.ts'.

最終結(jié)果

======== Module name 'typescript' was successfully resolved to 'node_modules/typescript/lib/typescript.d.ts'. ========

使用--noResolve

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

--noResolve編譯選項告訴編譯器不要添加任何不是在命令行上傳入的文件到編譯列表。 編譯器仍然會嘗試解析模塊纺荧,但是只要沒有指定這個文件旭愧,那么它就不會被包含在內(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宙暇,因為它在命令行上指定了输枯。

找不到moduleB,因為沒有在命令行上傳遞占贫。

常見問題

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

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

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

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末设凹,一起剝皮案震驚了整個濱河市舰讹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌闪朱,老刑警劉巖月匣,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钻洒,死亡現(xiàn)場離奇詭異,居然都是意外死亡锄开,警方通過查閱死者的電腦和手機素标,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來萍悴,“玉大人头遭,你說我怎么就攤上這事⊙⒂眨” “怎么了计维?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長撕予。 經(jīng)常有香客問我鲫惶,道長,這世上最難降的妖魔是什么实抡? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任欠母,我火速辦了婚禮,結(jié)果婚禮上吆寨,老公的妹妹穿的比我還像新娘赏淌。我一直安慰自己,他們只是感情好鸟废,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布猜敢。 她就那樣靜靜地躺著,像睡著了一般盒延。 火紅的嫁衣襯著肌膚如雪缩擂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天添寺,我揣著相機與錄音胯盯,去河邊找鬼。 笑死计露,一個胖子當著我的面吹牛博脑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播票罐,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼叉趣,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了该押?” 一聲冷哼從身側(cè)響起疗杉,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蚕礼,沒想到半個月后烟具,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體梢什,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年朝聋,在試婚紗的時候發(fā)現(xiàn)自己被綠了嗡午。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡冀痕,死狀恐怖荔睹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情言蛇,我是刑警寧澤应媚,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站猜极,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏消玄。R本人自食惡果不足惜跟伏,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望翩瓜。 院中可真熱鬧受扳,春花似錦、人聲如沸兔跌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坟桅。三九已至华望,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間仅乓,已是汗流浹背赖舟。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留夸楣,地道東北人宾抓。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像豫喧,于是被迫代替她去往敵國和親石洗。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容