模块、命名空间
几种模块方式:
- ES模块:ES模块是ECMAScript2015(ES6)语言规范的一部分,每一个(包含export/import的)JavaScript文件都是一个模块,在模块中定义的变量的作用域被限制在了文件中。
babel 转 ES Module 例子:
import Select from './Select';
import Option from './Option';
import OptGroup from './OptGroup';
export { Option, OptGroup };
转换后:
var _Select = _interopRequireDefault(require("./Select"));
var _Option = _interopRequireDefault(require("./Option"));
var _OptGroup = _interopRequireDefault(require("./OptGroup"));
var _default = _Select.default;
exports.default = _default;
- CommonJS:在JavaScript只能编写网页应用的时候,CommonJS提供了一些API,能够让JavaScript编写不同的JavaScript解释器和主机运行环境的程序。CommonJS一般用于服务端(Node.js)。CommonJS的一个模块就是一个脚本文件。require 命令第一次加载该脚本时,就会执行整个脚本,然后在内存中生成一个对象。
{
id: '...', // 模块名
exports: { ... }, // 该模块导出的接口
loaded: true, // 表示模块是否加载完毕
...
}
// 需要用到这个模块时,就会到exports属性上取值。即使再次执行require命令,也不会再次执行该模块,而是到缓存中取值
CommonJS是同步加载模块,所有的模块都放在本地硬盘。等待模块时间就是硬盘读取文件时间,很小。对于浏览器而言,它需要从服务器加载模块,涉及到网速,代理等原因,一旦等待时间过长,浏览器处于”假死”状态,所以不适合浏览器使用。
exports.add = function(a, b) {
return a + b;
}
var math = require('math');
math.add(2, 3); // 5
- AMD:全称:Asynchronous Module Definition,即“异步模块定义”,采用异步方式加载模块,模块的加载不影响它后面语句的运行。这里异步指的是不堵塞浏览器其他任务(dom构建,css渲染等),而加载内部是同步的(加载完模块后立即执行回调)。AMD采用require命令加载模块,但是不同于CommonJS,它要求两个参数:
require([module], callback);
- callback: 回调函数中参数对应数组中的成员(模块)
require(['math'], function(math) {
math.add(2, 3);
})
模块书写必须使用特定的define()函数来定义
define(function() {
var add = function(x, y) {
return x + y;
}
return {
add: add
}
})
加载方式:
require(['math'], function(math) { alert(math.add(1, 1)); })
- UMD:(Universal Module Definition 通用模块定义)是一种模式,提供对当今最流行的脚本加载器的兼容。UMD主要支持的是AMD和CommonJS
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery', 'underscore'], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS之类的
module.exports = factory(require('jquery'), require('underscore'));
} else {
// 浏览器全局变量(root 即 window)
root.returnExports = factory(root.jQuery, root._);
}
}(this, function ($, _) {
// 方法
function a(){}; // 私有方法,因为它没被返回 (见下面)
function b(){}; // 公共方法,因为被返回了
function c(){}; // 公共方法,因为被返回了
// 暴露公共方法
return {
b: b,
c: c
}
}));
- SystemJS:
SystemJS是可配置的模块加载器。使用System.register方法注册模块,使用System.import方法导入模块。
System.register(["./mod"], function (exports_1) {
var mod_1;
var t;
return {
setters: [
function (mod_1_1) {
mod_1 = mod_1_1;
},
],
execute: function () {
exports_1("t", (t = mod_1.something + 1));
},
};
});
declare const System: any;
import { ZipCodeValidator as Zip } from "./ZipCodeValidator";
if (needZipValidation) {
System.import("./ZipCodeValidator").then((ZipCodeValidator: typeof Zip) => {
var x = new ZipCodeValidator();
if (x.isAcceptable("...")) {
/* ... */
}
});
}
# 普通模块
TypeScript模块和ES模块基本一样,只是它可以导出ES模块中没有的一些东西,比如类型别名和接口
# 环境模块
环境模块和普通模块不同,环境模块中声明的内容是全局的,环境模块(以及环境命名空间)的声明都不能使用export导出,也不能使用任何import ...语句,否则环境模块就会失效。根据这个问答,可以使用import()方法动态地在环境模块中导入其他模块。
declare一般用于声明外部模块
# 通配符模块声明
在TS中没有办法使用非代码资源,例如图片、视频等
declare moudle "*.png";
# 命名空间
- 直接使用时,注意会报错:命名空间没有被标记为仅声明类型。 非声明性命名空间仅在Babel中实验支持。所以我们需要安装一个插件:@babel/plugin-transform-typescript。
在.babelrc.js 加上
module.exports = {
plugins: [
["@babel/plugin-transform-typescript", {"allowNamespaces": true}] // allowNamespaces表示允许使用命名空间。
],
};
- eslint 报错: ES2015 module syntax is preferred over custom TypeScript modules and namespaces @typescript-eslint/no-namespace
在.eslintrc.js 关掉类型检查
rules: {
'@typescript-eslint/no-namespace': 'off'
},
# 三斜杠指令
- 三斜杠指令是包含单个 XML 标记的单行注释。注释的内容用作编译器指令
- 三斜杠指令仅在其包含文件的顶部有效。三斜杠指令前只能有单行或多行注释,包括其他三斜杠指令。如果在语句或声明之后遇到它们,它们将被视为常规单行注释,并且没有特殊含义
- 作用:用作文件之间依赖关系的声明,跨文件进行分割
- 注意:需要关掉检查'@typescript-eslint/triple-slash-reference': 'off'
/// <reference path="..." />