说明
在 JavaScript 中,可以自定义模块,模块可以导出,在需要引用的地方进行导入。
在 NodeJs 中,内置的模块都遵循 CommonJS 规范,导出语法为:module.exports、exports(助记:exports 末尾有s)。
在 TypeScript 中,有 export、export default 等导出方式(助记:export 末尾没有s)。
CommonJs 中的导入与导出
导出语法为:module.exports、exports(助记:exports 末尾有s)。
可以使用 require 进行导入。
案例
定义 maths.js
function absolute(num:number){
if (num<0) return num * (-1);
return num;
}
module.exports = {
pi:3.14,
squareTwo: 1.41,
phi: 1.61,
absolute
}
或
function absolute(num:number){
if (num<0) return num * (-1);
return num;
}
exports.absolute = absolute;
或
function absolute(num:number){
if (num<0) return num * (-1);
return num;
}
exports = {
pi:3.14,
squareTwo: 1.41,
phi: 1.61,
absolute
}
其他
module.exports = function () {}
exports.site = 'https://www.baidu.com'
module.exports.name = 'tomcat'
定义引用文件 main.js,使用第一种 maths.js 文件。
const maths = require('./maths');
console.log(maths.pi);
说明
很容易能够看出,模块导出时,是将要导出的对象绑定到 exports 上。
在引入的时候,require 引入了 exports 对象,定义一个变量,就可以操作该对象了。
模块中的 exports 变量指向 module.exports 。
ES Modules 中的导入与导出
在 ECMAScript 6 中,又新增了语法 export、export default(助记:export 末尾没有 s)。
常见使用如下所示。
export default function () {}
export const site = 'https://www.baidu.com'
export const name = 'tomcat'
想要清晰的了解 es module 中的各种 export 导出方式,可以从编译后的角度来看。编译后,都是 CommonJS 的形式,很容易理解。
下面用 TypeScript 的案例,使用 tsc 进行编译,查看编译后的内容,来分析各种 export 、import 。
export default 和 export
定义 Calculator.ts
export default class Calculator {
public add(num1, num2) {
return num1 + num2;
}
}
export class greet {
public hello() {
console.log("Hello,World!");
}
}
export class shopping {
public buyTv() {
console.log("I want to buy a tv.");
}
}
编译后 Calculator.js
"use strict";
exports.__esModule = true;
exports.shopping = exports.greet = void 0;
var Calculator = /** @class */ (function () {
function Calculator() {
}
Calculator.prototype.add = function (num1, num2) {
return num1 + num2;
};
return Calculator;
}());
exports["default"] = Calculator;
var greet = /** @class */ (function () {
function greet() {
}
greet.prototype.hello = function () {
console.log("Hello,World!");
};
return greet;
}());
exports.greet = greet;
var shopping = /** @class */ (function () {
function shopping() {
}
shopping.prototype.buyTv = function () {
console.log("I want to buy a tv.");
};
return shopping;
}());
exports.shopping = shopping;
引入 MyMain.ts
import MyCal, {greet, shopping} from './Calculator';
let calc = new MyCal();
console.log(calc.add(1, 2));
let myGreet = new greet();
myGreet.hello();
let myShopping = new shopping();
myShopping.buyTv();
编译后 MyMain.js
"use strict";
exports.__esModule = true;
var Calculator_1 = require("./Calculator");
var calc = new Calculator_1["default"]();
console.log(calc.add(1, 2));
var myGreet = new Calculator_1.greet();
myGreet.hello();
var myShopping = new Calculator_1.shopping();
myShopping.buyTv();
可以看到,在 Calculator.ts 中定义的类(方法也类似),如果使用 export default 方式导出,就会被挂载到 module.exports 对象的 "default" 属性对象上,因此最多只能有一个 export default 方式导出的对象,可以有多个 export 方式导出的对象;
如果使用 export 方式导出,就会被直接挂载到 module.exports 对象上,属性名是各个被挂载的类或方法名。
使用 export default 方式导出的类或方法的定义,可以使用任意的名称接收:
export default function helloWorld (){
console.log ('Hello,World');
}
import hello from './hello'
因为编译后,定义的任意名称,都会被转换成直接读取 exports["default"] 对象属性。
使用 export 方式导出的类或方法的定义,可以使用解构的方式接收:
export var pi = 3.14;
export let squareTwo = 1.41;
export const phi = 1.61;
export class RandomNumberGenerator{}
export function absolute(num:number){
if (num<0) return num * (-1);
return num;
}
import {pi,phi,absolute} from './maths';
使用别名
export default 方式导出时,使用别名引入:
import * as MyCalAlias from './Calculator';
let ma = new MyCalAlias.default();
ma.add(1,2);
export 导出模块时,使用解构的方式引入,可以自定义别名引入:
import {greet as nGreet, shopping as nShopping} from './Calculator';
let nGreetObj = new nGreet();
nGreetObj.hello();
let nShoppingObj = new nShopping();
nShoppingObj.buyTv();
编译后的结果
var MyCalAlias = require("./Calculator");
var ma = new MyCalAlias["default"]();
ma.add(1, 2);
var Calculator_2 = require("./Calculator");
var nGreetObj = new Calculator_2.greet();
nGreetObj.hello();
var nShoppingObj = new Calculator_2.shopping();
nShoppingObj.buyTv();
可以看到,使用 export default 方式导出后,使用别名引入时,别名操作的是 module.exports 中的 default 属性;
使用 export 方式导出后,使用别名引入时,别名操作的是 module.exports 中对应的对象。
export var pi = 3.14;
export default class RandomNumberGenerator{}
// 解构中指定别名
import {pi as p} from './maths';
// 其中解构部分为非default,非解构部分为 export default
import {pi as p},RNGen from './maths';
const rnGen = new RNGen();
// 全局指定别名
import * as math from './maths';
console.log(math.pi);
const rnGen = new math.default();
TypeScript 特定的 ES 模块语法
有些 ES 模块语法,是 TypeScript 特有的。
自定义类型的导入、导出
在 TypeScript 中,可以导出、导入自定义的类型。
animals.ts
export type Cat{
breed: string,
yearOfBirth: number
}
export interface Dog{
breed: string,
yearOfBirth: number
}
export const createCatName = () => "littleCat";
导入类型 main.ts
import type { Cat, Dog } from './animals';
import { type Cat, type Dog, createCatName } from './animals';
type Animals = Cat | Dog;
TypeScript 与 require
TypeScript 有 ES-MODULES 这样的语法,它直接可以与 CommonJS 和 amd 的 require 相关联,
使用 ES-MODULES 的 import ,在大多数情况下,和这些环境的 require 是相同的。
这种语法保证,在 TypeScript 文件中,与 CommonJS 输出有一对一的匹配。
import fs = require("fs");
const code = fs.readFileSync ("hello.ts","utf8");
其他语法
除了上面常用的导入、导出语法,还有一些其他的语法形式。
const ... = require('./module') 与 import ... from './module'
这两种形式,最终编译后的结果是一样的。
例如:
const myData = require('./module');
import myData = require('./module');
最终会转换成 var myData = require('./module')。
export=
定义 IndividualModule.ts
export = class DirectGoal {
public goal() {
console.log("我有一个目标!");
}
}
在一个模块中,只能有一个 export = ,因为如果有多个,则会覆盖 module.exports 对象。
引入
import MyGoal = require('./IndividualModule');
let myGoal = new MyGoal();
myGoal.goal();
编译后
var MyGoal = require("./IndividualModule");
var myGoal = new MyGoal();
myGoal.goal();
作用与 module.exports = class DirectGoal {} 一致。