TS学习(六) :TS的模块化使用

在讲模块化之前,我们线了解一些前端领域中有哪些模块化标准: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="fcc study ts"
 
//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:'fcc study ts',
    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 = "fcc study ts";
 
//编译后的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="fcc study ts"
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 = "fcc study ts";
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="fcc study ts"
export default function (){
    console.log("Hello myModule!")
}
 
//编译后的结果
export function sum(a, b) {
    return a + b;
}
export let name = "fcc study ts";
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:'fcc study ts',
    sum(a:number,b:number):number{
        return a+b
    }
}


编译后的结果与原来的是没有变化的

//编译后的文件 myModule.ts
module.exports = {
    name: 'fcc study ts',
    sum(a, b) {
        return a + b;
    }
};


然而在导入时,会发现使用commonjs导入,const myModule = require("./myModule")没有类型检查,它检查的结果是any类型,编译后的结果也是一样的

如果想要获取类型检查,最好使用es6的模块化导入导出,当然如果你还是想要使用commonjs进行导入导出,你可以进行如下操作

在导出的时候你不要使用 module.exports ={}的方式导出,而是使用export={}的方式进行导出,编译的结果和使用module.exports ={}的方式是一样的,不同的地方就是使用export={}有类型检查

//原文件 myModule.ts
export = {
    name:'fcc study ts',
    sum(a:number,b:number):number{
        return a+b
    }
}
//编译后的文件 myModule.js
module.exports = {
    name: 'fcc study ts',
    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目录下当前文件夹有没有该文件,如果没有就一层一层网上找

  • 19
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值