javascript从es5之前都缺少一种模块机制,无法通过js引入文件,于是requirejs等等的加载器应运而生。这些加载器的使用也并不统一,产生了amd,commonjs,umd等等的规范,各有所长,直到es6的发布,js自身引入的模块机制,将会在以后逐渐被应用起来。

Typescrit的模块机制与es6的模块基本类似,也提供了转换为amd,es6,umd,commonjs,system的转换,一会我们通过一个简单的例子来运行一下ts的模块。

ts特殊的内联标签。

/// <reference path="revence.ts" /> 

三个斜线,reference标签,path指向引入的文件,引入的文件呢,需要定义一个命名空间来引用定义的东西。

revence.ts

 namespace Validation {
const ac = "abc";
export function a(){
console.log("module be loaded");
}
}

定义一个名为Validation的命名空间,导出用export导出。这里Validation下的a函数被导出,引用的时候就可以用Validation.a()来引用它。

text.ts

/// <reference path="revence.ts" />
Validation.a();

main文件是这个text.ts,引用了revence文件。编译器怎么编译它呢?

两种方法:一,把所有的输入文件编译为一个输出文件,需要使用--outFile标记:

tsc text.ts --outFile a.js  

这样输出文件都会在a.js中,网页script引入了。

    二,我们可以编译每一个文件(默认方式),那么每个源文件都会对应生成一个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。编译为其他模块标准。

SimpleModule.ts
import m = require("mod");
export let t = m.something + 1;
AMD / RequireJS SimpleModule.js
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;
UMD SimpleModule.js
(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

示例:Node.js里的动态模块加载
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 classexport function (考虑使用export default
  • 多个文件的顶层具有同样的export namespace Foo { (不要以为这些会合并到一个Foo中!)

Typescript学习笔记(五) 模块机制的更多相关文章

  1. Typescript 学习笔记五:类

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

  2. NDK学习笔记(五)Reader机制

    针对每一种后缀名Nuke都提供了对应的模块.为了决定用哪个版本的reader或writer模块,Nuke会先解析文件后缀名再以此为依据调用相关模块. 以JPG为例: 该文件格式有两种后缀名:.jpg和 ...

  3. python学习笔记五 模块上(基础篇)

    模块学习 模块,用一砣代码实现了某个功能的代码集合. 类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合.而对于一个复杂的功能来,可能需要 ...

  4. python学习笔记五 模块下(基础篇)

    shevle 模块 扩展pickle模块... 1.潜在的陷进 >>> import shelve>>> s = shelve.open("nb" ...

  5. NodeJS学习笔记 (23)模块机制-module

    https://github.com/chyingp/nodejs-learning-guide

  6. Typescript 学习笔记七:泛型

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

  7. Typescript 学习笔记六:接口

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

  8. Typescript 学习笔记四:回忆ES5 中的类

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

  9. Typescript 学习笔记二:数据类型

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

  10. Typescript 学习笔记三:函数

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

随机推荐

  1. React Native之FlatList的介绍与使用实例

    React Native之FlatList的介绍与使用实例 功能简介 FlatList高性能的简单列表组件,支持下面这些常用的功能: 完全跨平台. 支持水平布局模式. 行组件显示或隐藏时可配置回调事件 ...

  2. oracle查询不走索引的一些情况(索引失效)

    Oracle建立索引的目的是为了避免全表扫描,提高查询的效率. 但是有些情况下发现即使建立了索引,但是写出来的查询还是很慢,然后会发现是索引失效导致的,所以需要了解一下那些情况会导致索引失效,即查询不 ...

  3. Linux的基本解读

    Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多用户.多任务.支持多线程和多CPU的操作系统 而严格来讲,Linux这个词本身只表示Linux内核,但实际上人 ...

  4. [转帖]Windows NT 之父 - David Cutler

    Windows NT 之父 - David Cutler https://www.cnblogs.com/wangwust/p/6826200.html 曾经下过 夺路狂奔的电子书 但是还没看完.. ...

  5. 120. 单词接龙 (BFS)

    描述 给出两个单词(start和end)和一个字典,找到从start到end的最短转换序列 比如: 每次只能改变一个字母. 变换过程中的中间单词必须在字典中出现. 如果没有转换序列则返回0. 所有单词 ...

  6. Latex常用

    插入罗马数字 \newcommand{\RNum}[1]{\uppercase\expandafter{\romannumeral #1\relax}} 然后在正文里面就可以用\RNum{}来添加罗马 ...

  7. Day2 列表,元组,字典,集合

    一,列表 定义:[]内以逗号分隔,按照索引,存放各种数据类型,每个位置代表一个元素. list=['alex', 'jack', 'chen', 'shaoye'] #创建一个列表. 特性: 1.可存 ...

  8. Vue2.0 子组件和父组件之间的传值

    Vue是一个轻量级的渐进式框架,对于它的一些特性和优点在此就不做赘述,本篇文章主要来探讨一下Vue子父组件通信的问题 首先我们先搭好开发环境,我们首先得装好git和npm这两个工具(如果有不清楚的同学 ...

  9. 字符串正则替换replace第二个参数是函数

    zepto中 //将字符串转成驼峰式的格式 camelize = function (str) { return str.replace(/-+(.)?/g, function (match, chr ...

  10. spring-01

    Spring概述 概述 Spring是一个开源框架 为企业级开发而生 是一个IOC[DI]和AOP容器框架 有许多优良特性 非侵入式:基于Spring开发的应用中的对象可以不依赖Spring的API. ...