javascript从es5以前都缺乏一种模块机制,没法经过js引入文件,因而requirejs等等的加载器应运而生。这些加载器的使用也并不统一,产生了amd,commonjs,umd等等的规范,各有所长,直到es6的发布,js自身引入的模块机制,将会在之后逐渐被应用起来。javascript
Typescrit的模块机制与es6的模块基本相似,也提供了转换为amd,es6,umd,commonjs,system的转换,一会咱们经过一个简单的例子来运行一下ts的模块。html
ts特殊的内联标签。java
/// <reference path="revence.ts" />
三个斜线,reference标签,path指向引入的文件,引入的文件呢,须要定义一个命名空间来引用定义的东西。node
revence.tswebpack
namespace Validation { const ac = "abc"; export function a(){ console.log("module be loaded"); } }
定义一个名为Validation的命名空间,导出用export导出。这里Validation下的a函数被导出,引用的时候就能够用Validation.a()来引用它。es6
text.tsweb
/// <reference path="revence.ts" /> Validation.a();
main文件是这个text.ts,引用了revence文件。编译器怎么编译它呢?typescript
两种方法:一,把全部的输入文件编译为一个输出文件,须要使用--outFile
标记:函数
tsc text.ts --outFile a.js
这样输出文件都会在a.js中,网页script引入了。requirejs
二,咱们能够编译每个文件(默认方式),那么每一个源文件都会对应生成一个JavaScript文件。 而后,在页面上经过<script>
标签把全部生成的JavaScript文件按正确的顺序引进来,好比:
tsc --outFile sample.js Validation.ts LettersOnlyValidator.ts ZipCodeValidator.ts Test.ts
<script src="Validation.js" type="text/javascript" /> <script src="LettersOnlyValidator.js" type="text/javascript" /> <script src="ZipCodeValidator.js" type="text/javascript" /> <script src="Test.js" type="text/javascript" />
接下来就是ts的模块机制,而非script加载机制了。
关键词:import,export。同es6。
经过export关键词导出,包括interface的任何东西都会被导出。好比
export const numberRegexp = /^[0-9]+$/;
export interface StringValidator { isAcceptable(s: string): boolean; }
export class ParseIntBasedZipCodeValidator { isAcceptable(s: string) { return s.length === 5 && parseInt(s).toString() === s; } }
不用关心导出内容是什么,反正经过export已把这些东西都导出了。
1。从新导出。as至关于重命名,from以后的是导入的地址,这至关于从./ZipCodeValidator中把ZipCodeValidator导入,重命名为RegExpBasedZipCodeValidator再导出。记住用法便可。
// 导出原先的验证器但作了重命名 export {ZipCodeValidator as RegExpBasedZipCodeValidator} from "./ZipCodeValidator";
2。整合导出。*至关于所有导出。下面代码整合了3个文件,并所有导出。
export * from "./StringValidator"; // exports interface StringValidator export * from "./LettersOnlyValidator"; // exports class LettersOnlyValidator export * from "./ZipCodeValidator"; // exports class ZipCodeValidator
导入关键字是import。
import基本用法
import { ZipCodeValidator } from "./ZipCodeValidator"; let myValidator = new ZipCodeValidator();
这里./ZipCodeValidator导出的东西里面有个名字叫ZipCodeValidator的,用它的名字导入。这里{xxx}={123},就是这种形式,其实用到了es6的解构赋值。不懂的转战es6。调用用其名字直接调用就能够了。
1。导入重命名
import { ZipCodeValidator as ZCV } from "./ZipCodeValidator"; let myValidator = new ZCV();
as语法,重命名为ZCV
2。导入所有
import * as validator from "./ZipCodeValidator"; let myValidator = new validator.ZipCodeValidator();
这里导入了./ZipCodeValidator文件的全部导出,而不是上个例子仅导入ZipCodeValidator。as validator至关于给了全部导出一个名字,能够理解为全部导出的命名空间。
Tips:
尽管不推荐这么作,一些模块会设置一些全局状态供其它模块使用。 这些模块可能没有任何的导出或用户根本就不关注它的导出。 使用下面的方法来导入这类模块:
import "./my-module.js";
3。默认导出。default
好比
declare let $: JQuery; export default $;
引用
import $ from "JQuery";
$("button.continue").html( "Next Step..." );
default只能导出一项,仅导出一个函数,或者一个接口,或者任何一个单项的东西,default是最佳实践。由于在import的时候不指定导入的东西默认导入default。看下面这个例子
export default class ZipCodeValidator { static numberRegexp = /^[0-9]+$/; isAcceptable(s: string) { return s.length === 5 && ZipCodeValidator.numberRegexp.test(s); } }
import validator from "./ZipCodeValidator"; let validator = new validator();
4。export =
和 import = require()
TypeScript模块支持export =
语法,以配合传统的CommonJS和AMD的工做流。
export =
语法定义一个模块的导出对象。 它能够是类,接口,命名空间,函数或枚举。
若要导入一个使用了export =
的模块时,必须使用TypeScript提供的特定语法import let = require("module")
。
let numberRegexp = /^[0-9]+$/; class ZipCodeValidator { isAcceptable(s: string) { return s.length === 5 && numberRegexp.test(s); } } export = ZipCodeValidator;
import zip = require("./ZipCodeValidator"); // Some samples to try let strings = ["Hello", "98052", "101"]; // Validators to use let validator = new zip.ZipCodeValidator(); // Show whether each string passed each validator strings.forEach(s => { console.log(`"${ s }" - ${ validator.isAcceptable(s) ? "matches" : "does not match" }`); });
5。编译为其余模块标准。
import m = require("mod");
export let t = m.something + 1;
define(["require", "exports", "./mod"], function (require, exports, mod_1) { exports.t = mod_1.something + 1; });
CommonJS / Node SimpleModule.js
let mod_1 = require("./mod");
exports.t = mod_1.something + 1;
(function (factory) { if (typeof module === "object" && typeof module.exports === "object") { let v = factory(require, exports); if (v !== undefined) module.exports = v; } else if (typeof define === "function" && define.amd) { define(["require", "exports", "./mod"], factory); } })(function (require, exports) { let mod_1 = require("./mod"); exports.t = mod_1.something + 1; });
System SimpleModule.js
System.register(["./mod"], function(exports_1) { let mod_1; let t; return { setters:[ function (mod_1_1) { mod_1 = mod_1_1; }], execute: function() { exports_1("t", t = mod_1.something + 1); } } });
Native ECMAScript 2015 modules SimpleModule.js
import { something } from "./mod";
export let t = something + 1;
命令行的命令就是加 --module或者-m加要编译的规范名称。好比编译为amd
tsc text.ts -m amd
用tsc --help能够看到-m的各项参数。
高级加载
typescript的按需加载。也叫动态加载。看我博客前几篇的webpack中,用的require.ensure作按需加载,感受比较麻烦。然而ts的按需加载的简单得益于它的省略引用。即:
编译器会检测是否每一个模块都会在生成的JavaScript中用到。 若是一个模块标识符只在类型注解部分使用,而且彻底没有在表达式中使用时,就不会生成require
这个模块的代码。 省略掉没有用到的引用对性能提高是颇有益的,并同时提供了选择性加载模块的能力。
这种模式的核心是import id = require("...")
语句可让咱们访问模块导出的类型。 模块加载器会被动态调用(经过require
)
declare function require(moduleName: string): any; import { ZipCodeValidator as Zip } from "./ZipCodeValidator"; if (needZipValidation) { let ZipCodeValidator: typeof Zip = require("./ZipCodeValidator"); let validator = new ZipCodeValidator(); if (validator.isAcceptable("...")) { /* ... */ } }
示例:require.js里的动态模块加载
declare function require(moduleNames: string[], onLoad: (...args: any[]) => void): void; import { ZipCodeValidator as Zip } from "./ZipCodeValidator"; if (needZipValidation) { require(["./ZipCodeValidator"], (ZipCodeValidator: typeof Zip) => { let validator = new ZipCodeValidator(); if (validator.isAcceptable("...")) { /* ... */ } }); }
只要if条件不成立,模块是不加载的。出个简单的例子:
text.ts
import m= require("revence");
export let t=1;;
revence.ts
export = { mod:1 }
编译为amd模块:根本没有引入刚才的模块。
define(["require", "exports"], function (require, exports) { exports.t = 1; ; });
如今改成text.ts
import m= require("revence");
export let t=m.mod+1;;
编译出来为
define(["require", "exports", "revence"], function (require, exports, m) { exports.t = m.mod + 1; ; });
模块加载的最佳实践
1。尽量地在顶层导出
用户应该更容易地使用你模块导出的内容。 嵌套层次过多会变得难以处理,所以仔细考虑一下如何组织你的代码。
2。模块里避免使用命名空间
模块中使用命名空间是没必要要的,在模块中导出的东西确定不能重名,而导入时使用者确定会为其命名或者直接使用,也不存在重名,使用命名空间是多余的。
3。若是仅导出单个 class
或 function
,使用 export default。
如刚才所说,default是比较好的实践。
4。若是要导出多个对象,把它们放在顶层里导出
export class SomeType { /* ... */ } export function someFunc() { /* ... */ }
5。导入时明确地列出导入的名字
import { SomeType, SomeFunc } from "./MyThings"; let x = new SomeType(); let y = someFunc();
6。导入大量模块时使用命名空间
注意是导入时。
export class Dog { ... }
export class Cat { ... }
export class Tree { ... }
export class Flower { ... }
import * as myLargeModule from "./MyLargeModule.ts"; let x = new myLargeModule.Dog();
7。使用从新导出进行扩展
你可能常常须要去扩展一个模块的功能。 JS里经常使用的一个模式是JQuery那样去扩展原对象。 如咱们以前提到的,模块不会像全局命名空间对象那样去合并。 推荐的方案是不要去改变原来的对象,而是导出一个新的实体来提供新的功能。
如下均为模块结构上的危险信号。从新检查以确保你没有在对模块使用命名空间:
export namespace Foo { ... }
(删除Foo
并把全部内容向上层移动一层)export class
或export function
(考虑使用export default
)export namespace Foo {
(不要觉得这些会合并到一个Foo
中!)