commonjs
特点:一个文件就是一个模块,拥有独立的作用域,适用于服务端不适合浏览器端。导出模块内部数据通过module.exports或exports对象默认导出:
// true
const a = 1
const b = 2
module.exports = {
a, b
}
或者
// true
const a = 1
const b = 2
exports.a = a
exports.b = b
module.exports等于exports这个对象,不能改变exports对象的指针方向。
// false
const a = 1
const b = 2
exports = {
a,
b
}
AMD
特点:浏览器端没有具体实现该规范的代码,需要使用define配合requireJS来实现。一个define就是一个模块。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="./require.js" data-main="./a.js"></script>
</head>
</html>
// 导出
// b.js
define(function () {
'use strict';
const c = 3
const d = 4
return {
c,
d
}
});
// 或者
define(function (require, exports, module) {
'use strict';
const c = 3
const d = 4
module.exports = {
c,
d
}
});
// 导入
// a.js 依赖前置
define(['./b'], function (b) {
console.log(b);
});
// 或者 后置依赖
define(function (require, exports, module) {
const b = require('./b')
console.log(b);
});
UMD
UMD的出现是为了同时兼容node端和浏览器端。
(function (root, factory) {
if (typeof module === 'object' && typeof module.exports === 'object') {
// node
module.exports = factory()
} else if (typeof define === 'function' && define.amd) {
// 浏览器
define(factory)
} else {
// 既不是浏览器也不是node
root.factory = factory
}
})(this, function () {
const a = 1
const b = 2
return {
a, b
}
})
ESM
一个文件就是模块,拥有独立的作用域,script标签需要声明type=“module”。
导出模块数据:
// 单个导出
export let a = 1
export let b = 2
// 导出列表
export {a,b}
// 重命名导出
export {a as one,b as two}
// 默认导出
export default {a,b}
export {a as default,b as two}
// 重定向导出
export * from './a'
export {a,b} from './a'
export { default } from './a'
TS模块系统
typescript有一套自己的模块系统,类似esm,以一个文件作为模块最小单元:
- 任何一个包含顶级import或者export的文件都被当成一个模块
- 如果一个文件不带有import 或者export ,那么它的内容就是全局的
全局模块(不推荐)
// index.ts
let a = 1
let b = 2
// index2.ts error
let a = 1
// index2.ts true
let a = 1
export default {}
当我们在两个不同的文件中重复声明同一个变量的时候如果没有使用顶级import或者export 那么就是全局声明导致重复声明。
导入js文件
// b.js commonjs导出方式
module.exports = {
a: {
b: 1
}
}
// a.js 默认引入
import m from './b.js'
console.log(m);
编译后会报错,此时配置allowSyntheticDefaultImports:true:
"use strict";
exports.__esModule = true;
var b_js_1 = require("./b.js");
console.log(b_js_1["default"]);
引入的方式是默认引入,需要默认导出才能引入(或者import * as m from './b.js' m={ a: [Getter], default: { a: { b: 1 } } }
)。使用默认导出编译后可以看出获取了该对象的default属性,commonjs的方式没有default。所以tsconfig.json可以配置esModuleInterop:true
的方式解决:
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var b_js_1 = __importDefault(require("./b.js"));
console.log(b_js_1.default);
模块解析规则
typescript支持两种不同的模块解析策略:Node、Classic。当module为:AMD、System、ES2015的时候,默认为Classic,否则为node。也可以设置moduleResolution为node。typescript现在使用了与Node.js类似的模块解析策略,但是Typescript增加了其他几个源文件扩展名的查找(.ts、.tsx、.d.ts),同时Typescript在package.json里使用types去表示main的意义。
Classic模块解析策略
- 相对导入
// src/a.ts
import a from './a.ts'
解析流程:
1.src/a.ts
默认补全后缀
import a from './a'
解析流程:
1./src/a.ts
2./src/a.d.ts
- 非相对导入
import a from 'a'
从包含文件目录开始依次向上级目录遍历查找,直到根目录
1./src/a.ts
2./src/a.d.ts
3./a.ts
4./a.d.ts
Node解析策略
- 相对导入
// src/a.js
import a from './a'
解析流程:
1.src/a.js
2.src/package.json中指定的文件
3.src/index.js
- 非相对导入
非相对导入中会在node_modules里查找,并从当前目录的node_modules目录下逐级向上级文件夹查找。
// src/a.js
import a from 'a'
解析流程:
1.src/node_modules/a.js
2.src/node_modules/package.json中main指定的文件
3.src/node_modules/index.js
4.node_modules/a.js
5.node_modules/package.json中main指定的文件
6.node_modules/index.js