系列文章会一直更新,大家可以收藏加关注。不懂的可以在评论区留言。
在*.ts文件中我们应该使用什么模块化方式去编写代码。
在正式介绍这个配置参数之前我们先看一下这个问题。
Typescript版本
Typescript | 5.5.2 |
module用来设置Typescript编译成javascript后所使用的模块系统。
{
compilerOptions: {
"module": "commonjs"
}
}
所以这个值要设置为你编译后的代码所在的运行环境的模块系统。
例子
我们看一下这个例子,我们有两个文件分别为:
- cjs.ts:commonjs导出(typescript中使用commonjs导出的语法和正常的不太一样)
- esm.ts: esm导出
我们的index.ts导入这两个模块,代码如下:
// cjs.ts
// typescript中的commonjs导出方式
export = {
a: 1
}
// esm.ts
export const b = 2;
// index.ts
import {b} from './esm';
// typescript中的commonjs导入方式
import a = require('./cjs');
console.log(a, b)
编译js的模块系统为commonjs
当我们的运行js的模块系统为commonjs时(比如node低版本的环境,新版本Node现在更推荐使用module: "Node16" 或 module: "NodeNext")
{
compilerOptions: {
"module": "commonjs"
}
}
编译后的代码如下:
// cjs.js
"use strict";
module.exports = {
a: 1
};
// esm.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.b = void 0;
exports.b = 2;
// index.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const esm_1 = require("./esm");
// import * as c from './cjs'
const a = require("./cjs");
console.log(a, esm_1.b);
所以在编写Typescript代码的时候,是可以使用esm和cjs的。
编译js的模块系统为ESM
ES2015/ES6
当我们运行js的环境为浏览器,并且浏览器只支持ES6语法时,我们可以设置为ES2015或ES6(一样)
{
compilerOptions: {
"module": "ES2015" // 或ES6
}
}
例子的代码编译后会报错:
ts/cjs.ts:1:1 - error TS1203: Export assignment cannot be used when targeting ECMAScript modules. Consider using 'export default' or another module format instead.
1 export = {
~~~~~~~~~~
2 a: 1
~~~~~~~~
3 }
~
ts/index.ts:3:1 - error TS1202: Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', or another module format instead.
3 import a = require('./cjs');
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Found 2 errors in 2 files.
Errors Files
1 ts/cjs.ts:1
1 ts/index.ts:3
在这里我们可以看到commonjs的模块导入导出会报错。(这个错误在ES2015(ES6),ES2020,ES2022,ESNext都会报)。
去掉cjs.ts的引用后,代码可以正常编译,编译结果如下:
// esm.js
export const b = 2;
// index.js
import { b } from './esm';
console.log(b);
ES2020
ES2020比ES2015多了dynamic imports和import.meta的支持,Typescript代码如下:
// index.ts
// 动态import
import('./esm').then(({b}) => {
console.log(b);
// import.meta
console.log(import.meta.url);
});
编译结果如下:
// esm.js
export const b = 2;
// index.js
// 动态import
import('./esm').then(({ b }) => {
console.log(b);
// import.meta
console.log(import.meta.url);
});
export {};
ES2022(ESNext)
现在ES2022和ESNext是相同的(如果以后js有大更新,则ES2022将会被冻结, ESNext指向下一个版本),ES2022比ES2020多了顶级await(top-level await)的支持,Typescript代码如下:
// 顶级await
const { b } = await import('./esm');
export {};
编译结果如下:
// esm.js
export const b = 2;
// index.js
// 顶级await
const { b } = await import('./esm');
export {};
SystemJS
SystemJS也不支持CommonJS语法,Typescript代码如下:
import {b} from './esm';
console.log(b)
代码编译结果如下:
// esm.js
System.register([], function (exports_1, context_1) {
"use strict";
var b;
var __moduleName = context_1 && context_1.id;
return {
setters: [],
execute: function () {
exports_1("b", b = 2);
}
};
});
// index.js
System.register(["./esm"], function (exports_1, context_1) {
"use strict";
var esm_1;
var __moduleName = context_1 && context_1.id;
return {
setters: [
function (esm_1_1) {
esm_1 = esm_1_1;
}
],
execute: function () {
console.log(esm_1.b);
}
};
});
AMD
AMD支持CommonJS和ESM,Typescript代码如下:
import {b} from './esm';
// typescript中的commonjs导入方式
import a = require('./cjs');
console.log(a, b)
代码编译结果如下:
// cjs.js
define(["require", "exports"], function (require, exports) {
"use strict";
return {
a: 1
};
});
// esm.js
define(["require", "exports"], function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.b = void 0;
exports.b = 2;
});
// index.js
define(["require", "exports", "./esm", "./cjs"], function (require, exports, esm_1, a) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
console.log(a, esm_1.b);
});
UMD
UMD支持CommonJS和ESM,代码编译结果如下:
// cjs.js
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports"], factory);
}
})(function (require, exports) {
"use strict";
return {
a: 1
};
});
// esm.js
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.b = void 0;
exports.b = 2;
});
// index.js
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "./esm", "./cjs"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const esm_1 = require("./esm");
// typescript中的commonjs导入方式
const a = require("./cjs");
console.log(a, esm_1.b);
});
Node16(NodeNext)
当前版本Node16和NodeNext表现一致(如果以后Node做出重大改变,Node16将会被冻结,NodeNext则指向下一个个版本),当我们js的运行环境为Node12及以上版本(CJS和ESM双模块系统)的时候,可以使用。
模块解析规则如下
当package.json中没有设置type字段,或者type字段等于commonjs,则所有.ts文件模块系统被转换为CommonJS系统。
当package.json中设置type字段等于module,则.ts文件被解析为ESM模块。且.ts文件禁止使用commonjs。
.cts文件的导出系统必须是commonjs,且编译后的js文件后缀为.cjs。
.ets文件的导出系统必须是ESM,且编译后的js文件后缀为.ejs。