關(guān)于術(shù)語(yǔ)的一點(diǎn)說(shuō)明: 請(qǐng)務(wù)必注意一點(diǎn),TypeScript 1.5里術(shù)語(yǔ)名已經(jīng)發(fā)生了變化。 “內(nèi)部模塊”現(xiàn)在稱做“命名空間”。 “外部模塊”現(xiàn)在則簡(jiǎn)稱為“模塊”,這是為了與 ECMAScript 2015里的術(shù)語(yǔ)保持一致,(也就是說(shuō)
module X {
相當(dāng)于現(xiàn)在推薦的寫(xiě)法namespace X {
)。
這篇文章將概括介紹在TypeScript里使用模塊與命名空間來(lái)組織代碼的方法。 我們也會(huì)談及命名空間和模塊的高級(jí)使用場(chǎng)景,和在使用它們的過(guò)程中常見(jiàn)的陷阱。
查看模塊章節(jié)了解關(guān)于模塊的更多信息。 查看 命名空間章節(jié)了解關(guān)于命名空間的更多信息。
命名空間是位于全局命名空間下的一個(gè)普通的帶有名字的JavaScript對(duì)象。 這令命名空間十分容易使用。 它們可以在多文件中同時(shí)使用,并通過(guò) --outFile
結(jié)合在一起。 命名空間是幫你組織Web應(yīng)用不錯(cuò)的方式,你可以把所有依賴都放在HTML頁(yè)面的 <script>
標(biāo)簽里。
但就像其它的全局命名空間污染一樣,它很難去識(shí)別組件之間的依賴關(guān)系,尤其是在大型的應(yīng)用中。
像命名空間一樣,模塊可以包含代碼和聲明。 不同的是模塊可以 聲明它的依賴。
模塊會(huì)把依賴添加到模塊加載器上(例如CommonJs / Require.js)。 對(duì)于小型的JS應(yīng)用來(lái)說(shuō)可能沒(méi)必要,但是對(duì)于大型應(yīng)用,這一點(diǎn)點(diǎn)的花費(fèi)會(huì)帶來(lái)長(zhǎng)久的模塊化和可維護(hù)性上的便利。 模塊也提供了更好的代碼重用,更強(qiáng)的封閉性以及更好的使用工具進(jìn)行優(yōu)化。
對(duì)于Node.js應(yīng)用來(lái)說(shuō),模塊是默認(rèn)并推薦的組織代碼的方式。
從ECMAScript 2015開(kāi)始,模塊成為了語(yǔ)言內(nèi)置的部分,應(yīng)該會(huì)被所有正常的解釋引擎所支持。 因此,對(duì)于新項(xiàng)目來(lái)說(shuō)推薦使用模塊做為組織代碼的方式。
這部分我們會(huì)描述常見(jiàn)的命名空間和模塊的使用陷阱和如何去避免它們。
/// <reference>
一個(gè)常見(jiàn)的錯(cuò)誤是使用/// <reference>
引用模塊文件,應(yīng)該使用import
。 要理解這之間的區(qū)別,我們首先應(yīng)該弄清編譯器是如何根據(jù) import
路徑(例如,import x from "...";
或import x = require("...")
里面的...
,等等)來(lái)定位模塊的類型信息的。
編譯器首先嘗試去查找相應(yīng)路徑下的.ts
,.tsx
再或者.d.ts
。 如果這些文件都找不到,編譯器會(huì)查找 外部模塊聲明。 回想一下,它們是在 .d.ts
文件里聲明的。
myModules.d.ts
// In a .d.ts file or .ts file that is not a module:
declare module "SomeModule" {
export function fn(): string;
}
myOtherModule.ts
/// <reference path="myModules.d.ts" />
import * as m from "SomeModule";
這里的引用標(biāo)簽指定了外來(lái)模塊的位置。 這就是一些Typescript例子中引用 node.d.ts
的方法。
如果你想把命名空間轉(zhuǎn)換為模塊,它可能會(huì)像下面這個(gè)文件一件:
shapes.ts
export namespace Shapes {
export class Triangle { /* ... */ }
export class Square { /* ... */ }
}
頂層的模塊Shapes
包裹了Triangle
和Square
。 對(duì)于使用它的人來(lái)說(shuō)這是令人迷惑和討厭的:
shapeConsumer.ts
import * as shapes from "./shapes";
let t = new shapes.Shapes.Triangle(); // shapes.Shapes?
TypeScript里模塊的一個(gè)特點(diǎn)是不同的模塊永遠(yuǎn)也不會(huì)在相同的作用域內(nèi)使用相同的名字。 因?yàn)槭褂媚K的人會(huì)為它們命名,所以完全沒(méi)有必要把導(dǎo)出的符號(hào)包裹在一個(gè)命名空間里。
再次重申,不應(yīng)該對(duì)模塊使用命名空間,使用命名空間是為了提供邏輯分組和避免命名沖突。 模塊文件本身已經(jīng)是一個(gè)邏輯分組,并且它的名字是由導(dǎo)入這個(gè)模塊的代碼指定,所以沒(méi)有必要為導(dǎo)出的對(duì)象增加額外的模塊層。
下面是改進(jìn)的例子:
shapes.ts
export class Triangle { /* ... */ }
export class Square { /* ... */ }
shapeConsumer.ts
import * as shapes from "./shapes";
let t = new shapes.Triangle();
就像每個(gè)JS文件對(duì)應(yīng)一個(gè)模塊一樣,TypeScript里模塊文件與生成的JS文件也是一一對(duì)應(yīng)的。 這會(huì)產(chǎn)生一種影響,根據(jù)你指定的目標(biāo)模塊系統(tǒng)的不同,你可能無(wú)法連接多個(gè)模塊源文件。 例如當(dāng)目標(biāo)模塊系統(tǒng)為 commonjs
或umd
時(shí),無(wú)法使用outFile
選項(xiàng),但是在TypeScript 1.8以上的版本能夠使用outFile
當(dāng)目標(biāo)為amd
或system
。
更多建議: