本篇将介绍TypeScript里的模块,和使用方法。
在ECMAScript 2015标准里,JavaScript新增了模块的概念。TypeScript也沿用了这个概念。
一、模块的导入和导出
模块在其自身的作用域里执行,而不是在全局作用域里;这意味着定义在一个模块里的变量,函数,类等等在模块外部是不可见的,除非你明确地使用export之一导出它们。 相反,如果想使用其它模块导出的变量,函数,类,接口等的时候,你必须要导入它们,可以使用import之一。
模块是自声明的。在TypeScript里,两个模块之间的关系是通过在文件级别上使用import和export建立的。下面是一个基本例子:
animal.ts
1 export class Animal { 2 name: string; 3 show(): string { 4 return this.name; 5 } 6 }
app.ts
1 import {Animal} from './animal'; 2 let dog = new Animal(); 3 dog.name = '狗狗'; 4 dog.show();
上面的例子里,在animal.ts里声明了一个类Animal,通过export导出。在app.ts里,指定相对文件路径,通过import导入,就可以使用Animal类。
因为JavaScript存在两种不同的模块引用方式,在编译成JavaScript时,可以通过TypeScript配置文件tsconfig.json指定编译之后的模块引用方式
1 { 2 "compilerOptions": { 3 "target": "es5", 4 "noImplicitAny": false, 5 "module": "commonjs", // 模块引用方式。候选值有:commonjs、amd等 6 "removeComments": false, 7 "sourceMap": false, 8 "outDir": "js" 9 }, 10 "include":[ 11 "ts" 12 ], 13 "exclude": [ 14 "js" 15 ] 16 }
下面分别是使用不同的方式编译后的JavaScript文件内容
commonjs
1 "use strict"; 2 var Animal = (function () { 3 function Animal() { 4 } 5 Animal.prototype.show = function () { 6 return this.name; 7 }; 8 return Animal; 9 }()); 10 exports.Animal = Animal;
1 'use strict'; 2 var animal_1 = require('./animal'); 3 var dog = new animal_1.Animal(); 4 dog.name = '狗狗'; 5 dog.show();
amd
1 define(["require", "exports"], function (require, exports) { 2 "use strict"; 3 var Animal = (function () { 4 function Animal() { 5 } 6 Animal.prototype.show = function () { 7 return this.name; 8 }; 9 return Animal; 10 }()); 11 exports.Animal = Animal; 12 });
1 define(["require", "exports", './animal'], function (require, exports, animal_1) { 2 'use strict'; 3 var dog = new animal_1.Animal(); 4 dog.name = '狗狗'; 5 dog.show(); 6 });
二、导入和导出的重命名
模块导入和导出时默认使用的内部对象的名称。TypeScript也支持在导出前和导入后进行重命名。将上面的例子修改一下
animal.ts
1 class Animal { 2 name: string; 3 show(): string { 4 return this.name; 5 } 6 } 7 8 export {Animal as ANI};
app.ts
1 import {ANI as Animal} from './animal'; 2 let dog = new Animal(); 3 dog.name = '狗狗'; 4 dog.show();
导入和导出时,通过as关键字对模块进行重命名。
这个地方有一点要注意,当导出的模块重命名后,导入时重命名前的模块名要与导出重命名后的模块名保持一致,否则编译器将提示错误信息。以上面的这个例子为例,导出的模块被重命名为ANI,将此模块在另外一个文件app.ts里导入时,as关键字前面的模块名必须是ANI。
或者,如果不知道导入模块的名称,则可以用*号代替
1 import * as animal_module from './animal'; 2 let dog = new animal_module.ANI(); 3 dog.name = '狗狗'; 4 dog.show();
上面的例子里,对*号代替的所有模块进行重命名为animal_module,则通过animal_module对象可以访问到模块里导出的所有内容。
三、导出和导出多个对象
通常情况,模块里会定义多个类型对象,然后一并导出。而导入的模块里也可能会有多个
animal.ts
1 enum HairColor { 2 Golden, 3 Black, 4 White 5 } 6 7 class Animal { 8 name: string; 9 color: HairColor; 10 show(): string { 11 return this.name; 12 } 13 } 14 15 export {Animal, HairColor};
app.ts
1 import * as animal_module from './animal'; 2 let dog = new animal_module.Animal(); 3 dog.name = '狗狗'; 4 dog.color = animal_module.HairColor.Golden; 5 dog.show();
导出时,可以将要导出的类型对象重新组装成一个json对象,然后导出。导入后,可以通过重命名的模块对象访问里面的内容。
四、默认导出
一个模块的默认导出只能有一个
animal.ts
1 class Animal { 2 name: string; 3 show(): string { 4 return this.name; 5 } 6 } 7 8 export default Animal;
app.ts
1 import Animal from './animal'; 2 let dog = new Animal(); 3 dog.name = '狗狗'; 4 dog.show();
在上面的例子里,通过default关键字,将Animal类导出。与一般的导入不同的是,导入默认的导出模块时,可以直接指定导入的模块名称,而不需要用{}花括号括起来。
五、动态加载模块
因为在JavaScript里,模块加载方式分别有两种:CommonJS和AMD。在用TypeScript时,要根据最终编译生成JavaScript的方式的配置内容不同,编写不同的代码。
模块文件animal.ts
1 class Animal { 2 name: string; 3 show(): string { 4 return this.name; 5 } 6 } 7 8 export {Animal};
CommonJS方式引用:
app.ts
1 // 定义加载方法 2 declare function require(moduleName: string): any; 3 4 import {Animal} from './animal'; 5 6 if (true) { 7 let Animal_Type: typeof Animal = require('./animal'); 8 let dog = new Animal_Type(); 9 dog.name = '狗狗'; 10 dog.show(); 11 }
AMD方式引用:
app.ts
1 // 定义加载方法 2 declare function require(moduleNames: string[], onLoad: (...args: any[]) => void): void; 3 4 import {Animal} from './animal'; 5 6 if (true) { 7 require(["./animal"], (Animal_Type: typeof Animal) => { 8 let dog = new Animal_Type(); 9 dog.name = '狗狗'; 10 dog.show(); 11 }); 12 }