在讲模块化之前,我们线了解一些前端领域中有哪些模块化标准:ES6、commonjs、amd、umd、system、esnext(这个还没有正式成为模块化标准)
那么这么多模块化标准,我们在TS中所讨论的是ES6和commonjs,他们是如何书写模块化语句?书写完成后编译结果是如何的
TS中如何书写模块化语句
TS中,导入和到处模块化,同一使用ES6的模块化标准,如下例子
//myModule.ts文件
export function sum(a:number,b:number):number{
return a+b
}
export const name="井底的蜗牛"
//index.ts 文件
import {name, sum} from "./myModule";
console.log(sum(3,4))
console.log(name)
在使用导出的函数和变量时,你会发现它会自动帮你把import {name, sum} from "./myModule";
导入,不需要手动导入
当然前提条件是,在导出的时候,导出的是一个声明export 声明变量/函数
,而不是默认导出的形式export defailt {}
如:
//myModule.ts文件
export default {
name:'井底的蜗牛',
sum(a:number,b:number):number{
return a+b
}
}
因为默认导出的对象是没有名字的,所以在导入时,是可以更改导入的名字的,所以无法享受到它的自动导入
//index.ts 文件
import myModule from "./myModule";
console.log(myModule.sum(3,4))
console.log(myModule.name)
这里值得注意:在导入的时候,文件名字不要加后缀名import myModule from "./myModule.ts";
因为在编译结果中是没有ts文件的 你加入ts后缀编译后根本找不到该文件这样必定报错
在TS中使用模块还是挺简单的,就使用ES6的模块化标准就可以了
编译结果中的模块化
到了js代码中,世界其实没有那么清净的,本来我们写代码的时候都使用的是ES6模块化标准,但到了真实的世界是混乱的,一会儿是ES6标准,一会儿是commonjs标准,那么编译结果里面是怎么处理的,使用的是什么模块化标准呢?
首先编译结果是可以配置的,啥意思呢,就是编译结果使用ES6还是commonjs标准是可以进行配置的,既然可以配置那么在哪里配置呢,既然是配置那肯定是配置文件了
我们可以在TS的配置文件tsconfig.json
中进行配置,是哪个配置呢,就是module
这个配置
{
"compilerOptions": {
"target": "es2016",//配置编译目标代码的版本标准
"module": "commonjs",//配置编译目标使用的模块化标准
"lib": ["es2016"], //表示默认详情下ts使用的是那一个环境
"outDir": "./dist",//编译结果的目录
"strictNullChecks": true,//TS检查变量是否是null
"removeComments": true,/*编译结果中是否移除注释*/
"noImplicitUseStrict": true,/*编译结果中是否有"use strict"*/
},
"include":["./src/index.ts"],//执行ts的目录
}
配置编译结果为 commonjs时的情况
当我们配置为"module": "commonjs"
时,编译的结果使用的就是commonjs的标准,这里使用的是声明变量导出,编译的结果如
//编译后的myModule.js文件
Object.defineProperty(exports, "__esModule", { value: true });
exports.name = exports.sum = void 0;
function sum(a, b) {
return a + b;
}
exports.sum = sum;
exports.name = "井底的蜗牛";
//编译后的index.js文件
Object.defineProperty(exports, "__esModule", { value: true });
const myModule_1 = require("./myModule");
console.log((0, myModule_1.sum)(1, 4));
console.log(myModule_1.name);
当我们的文件使用的是默认导出,编译的结果如下:
在原文件myModule.ts 在该文件中加入一个默认导出一个函数
//原文件myModule.ts
export function sum(a:number,b:number):number{
return a+b
}
export let name="井底的蜗牛"
export default function (){
console.log("this is myModule!")
}
结果编译结果中就变成了这样,我们可以看出,由于commonjs导出的是一个exports对象,所以默认的导出会变成exports的default属性
//编译后的myModule.js
Object.defineProperty(exports, "__esModule", { value: true });
exports.name = exports.sum = void 0;
function sum(a, b) {
return a + b;
}
exports.sum = sum;
exports.name = "井底的蜗牛";
function default_1() {
console.log("Hello myModule!");
}
exports.default = default_1;
我们在导入时给默认导出的函数命名为sayHell
//原文件index.ts
import sayHell,{name, sum} from "./myModule";
console.log(sum(1,4))
console.log(name)
sayHell()
结果到编译结果中,commonjs导入的是myModule_1整个对象,而刚刚命名的默认导出的名字就变成了,myModule_1.default()执行,因为导出的时候就是这样导出的, 所以在导入的时候,不管你名字是啥,编译后都是myModule_1.default
//编译后的index.js
Object.defineProperty(exports, "__esModule", { value: true });
const myModule_1 = require("./myModule");
console.log((0, myModule_1.sum)(1, 4));
console.log(myModule_1.name);
(0, myModule_1.default)();
配置编译结果为 es6时的情况
在配置编译结果为es6时,发现导出时的编译结果和原文件一模一样
//原文件 myModule.ts
export function sum(a:number,b:number):number{
return a+b
}
export let name="井底的蜗牛"
export default function (){
console.log("Hello myModule!")
}
//编译后的结果
export function sum(a, b) {
return a + b;
}
export let name = "井底的蜗牛";
export default function () {
console.log("Hello myModule!");
}
导入时,不会像commonjs一样,把默认导入的名字给替换,它还是sayHello
//原文件 index.ts
import sayHell,{name, sum} from "./myModule";
console.log(sum(1,4))
console.log(name)
sayHell()
//编译结果
import sayHell, { name, sum } from "./myModule";
console.log(sum(1, 4));
console.log(name);
sayHell();
总结:TS中发模块化在编译结果中
- 如果编译结果的模块化标准是ES6:没有区别
- 如果编译结果的模块化标准是commonjs:
- 导出:导出的声明会变成exports的属性,默认的导出会变成exports的default属性;
- 导入:导入时给默认导出命的名字会变成,导出对象的default属性,而不是使用导入时的名字
温馨小提示
当我们在TS中导入node的一些包时,如import fs from 'fs'
,他可能会报错,提示说fs没有默认导出,
//原文件 index.ts
import fs from 'fs'
fs.readFileSync('./')
编译后的结果是长这样的,因为import fs from 'fs'
这个就相当于commonjs中的默认导入,而fs模块本身导出就是module.exports={}
这种形式 所以编译后就会变成一个对象的default属性,然后去default属性中找readFileSync方法,这当然没有所以报错
//编译后的文件 index.js
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = require("fs");
fs_1.default.readFileSync('./');
如何解决这种报错,有三种方法
- 1、在我们导入时使用
import {readFileSync} from 'fs'
这种按需导入的方式,就不会报错了,编译的结果如下
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = require("fs");
(0, fs_1.readFileSync)('./');
- 2、导入时
import * as fs from 'fs'
用这种方式进行导入,也可正常使用,当然编译结果和第一种是一样的
Object.defineProperty(exports, "__esModule", { value: true });
const fs = require("fs");
fs.readFileSync('./');
- 3、在tsconfig.json配置文件中加入
"esModuleInterop": true,
进行配置,它的作用就是启用es模块化交互非es模块导出, 然后我们还是使用import fs from 'fs'
,他就不报错了,然后我们看看编译结果 发现多了一个__importDefault
的辅助函数
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = __importDefault(require("fs"));
fs_1.default.readFileSync('./');
温馨提示2
如何在TS中使用commonjs?看下面例子
一般我们是这样用commonjs进行导出的
//导出原文件文件,myModule.ts
module.exports = {
name:'井底的蜗牛',
sum(a:number,b:number):number{
return a+b
}
}
编译后的结果与原来的是没有变化的
//编译后的文件 myModule.ts
module.exports = {
name: '井底的蜗牛',
sum(a, b) {
return a + b;
}
};
然而在导入时,会发现使用commonjs导入,const myModule = require("./myModule")
没有类型检查,它检查的结果是any类型,编译后的结果也是一样的
如果想要获取类型检查,最好使用es6的模块化导入导出,当然如果你还是想要使用commonjs进行导入导出,你可以进行如下操作
- 在导出的时候你不要使用
module.exports ={}
的方式导出,而是使用export={}
的方式进行导出,编译的结果和使用module.exports ={}
的方式是一样的,不同的地方就是使用export={}
有类型检查
//原文件 myModule.ts
export = {
name:'井底的蜗牛',
sum(a:number,b:number):number{
return a+b
}
}
//编译后的文件 myModule.js
module.exports = {
name: '井底的蜗牛',
sum(a, b) {
return a + b;
}
};
-
导入的时候也不要使用
const myModule = require("./myModule")
方式了,由于你在配置文件中添加了esModuleInterop
为true的配置,那么你就可以使用es6的方式进行导入
import myModule from './myModule'
如果你硬要使用require的方式进行导入,那么你可以这样写
import myModule = require("./myModule")
在使用的时候就可以看到其属性的类型了
当然在TS中最好使用ES6的模块化导入导出
模块化解析
模块化解析应该从什么位置寻找模块的呢
TS中,有两种模块解析策略
- classic: 经典(已过时了) -node:node解析策略,和node解析策略一样,不过唯一的变化就是将js替换为ts
- 相对路径:
require("./xxx")
,它先找当前目录下有没有这个文件,如果没有,就看package.json中有没有"main":"xxx.ts"
这个配置,他就会找./xxx
这个文件夹下有没有xxx.ts
这个文件,如果还是没有,就会去找这个文件夹下有没有index.xx
这个文件 - 非相对模块
require("xxx")
,它是去找node_modules目录下当前文件夹有没有该文件,如果没有就一层一层网上找
- 相对路径: