当前位置:   article > 正文

TS学习(六) :TS的模块化使用_object.defineproperty(exports

object.defineproperty(exports

在讲模块化之前,我们线了解一些前端领域中有哪些模块化标准:ES6、commonjs、amd、umd、system、esnext(这个还没有正式成为模块化标准)

那么这么多模块化标准,我们在TS中所讨论的是ES6和commonjs,他们是如何书写模块化语句?书写完成后编译结果是如何的

TS中如何书写模块化语句

TS中,导入和到处模块化,同一使用ES6的模块化标准,如下例子

  1. //myModule.ts文件
  2. export function sum(a:number,b:number):number{
  3. return a+b
  4. }
  5. export const name="井底的蜗牛"
  6. //index.ts 文件
  7. import {name, sum} from "./myModule";
  8. console.log(sum(3,4))
  9. console.log(name)

在使用导出的函数和变量时,你会发现它会自动帮你把import {name, sum} from "./myModule";导入,不需要手动导入

当然前提条件是,在导出的时候,导出的是一个声明export 声明变量/函数,而不是默认导出的形式export defailt {}如:

  1. //myModule.ts文件
  2. export default {
  3. name:'井底的蜗牛',
  4. sum(a:number,b:number):number{
  5. return a+b
  6. }
  7. }

因为默认导出的对象是没有名字的,所以在导入时,是可以更改导入的名字的,所以无法享受到它的自动导入

  1. //index.ts 文件
  2. import myModule from "./myModule";
  3. console.log(myModule.sum(3,4))
  4. console.log(myModule.name)

这里值得注意:在导入的时候,文件名字不要加后缀名import myModule from "./myModule.ts";因为在编译结果中是没有ts文件的 你加入ts后缀编译后根本找不到该文件这样必定报错

在TS中使用模块还是挺简单的,就使用ES6的模块化标准就可以了

编译结果中的模块化

到了js代码中,世界其实没有那么清净的,本来我们写代码的时候都使用的是ES6模块化标准,但到了真实的世界是混乱的,一会儿是ES6标准,一会儿是commonjs标准,那么编译结果里面是怎么处理的,使用的是什么模块化标准呢?

首先编译结果是可以配置的,啥意思呢,就是编译结果使用ES6还是commonjs标准是可以进行配置的,既然可以配置那么在哪里配置呢,既然是配置那肯定是配置文件了

我们可以在TS的配置文件tsconfig.json中进行配置,是哪个配置呢,就是module这个配置

  1. {
  2. "compilerOptions": {
  3. "target": "es2016",//配置编译目标代码的版本标准
  4. "module": "commonjs",//配置编译目标使用的模块化标准
  5. "lib": ["es2016"], //表示默认详情下ts使用的是那一个环境
  6. "outDir": "./dist",//编译结果的目录
  7. "strictNullChecks": true,//TS检查变量是否是null
  8. "removeComments": true,/*编译结果中是否移除注释*/
  9. "noImplicitUseStrict": true,/*编译结果中是否有"use strict"*/
  10. },
  11. "include":["./src/index.ts"],//执行ts的目录
  12. }

配置编译结果为 commonjs时的情况

当我们配置为"module": "commonjs"时,编译的结果使用的就是commonjs的标准,这里使用的是声明变量导出,编译的结果如

  1. //编译后的myModule.js文件
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.name = exports.sum = void 0;
  4. function sum(a, b) {
  5. return a + b;
  6. }
  7. exports.sum = sum;
  8. exports.name = "井底的蜗牛";
  9. //编译后的index.js文件
  10. Object.defineProperty(exports, "__esModule", { value: true });
  11. const myModule_1 = require("./myModule");
  12. console.log((0, myModule_1.sum)(1, 4));
  13. console.log(myModule_1.name);

当我们的文件使用的是默认导出,编译的结果如下:

在原文件myModule.ts 在该文件中加入一个默认导出一个函数

  1. //原文件myModule.ts
  2. export function sum(a:number,b:number):number{
  3. return a+b
  4. }
  5. export let name="井底的蜗牛"
  6. export default function (){
  7. console.log("this is myModule!")
  8. }

结果编译结果中就变成了这样,我们可以看出,由于commonjs导出的是一个exports对象,所以默认的导出会变成exports的default属性

  1. //编译后的myModule.js
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.name = exports.sum = void 0;
  4. function sum(a, b) {
  5. return a + b;
  6. }
  7. exports.sum = sum;
  8. exports.name = "井底的蜗牛";
  9. function default_1() {
  10. console.log("Hello myModule!");
  11. }
  12. exports.default = default_1;

我们在导入时给默认导出的函数命名为sayHell

  1. //原文件index.ts
  2. import sayHell,{name, sum} from "./myModule";
  3. console.log(sum(1,4))
  4. console.log(name)
  5. sayHell()

结果到编译结果中,commonjs导入的是myModule_1整个对象,而刚刚命名的默认导出的名字就变成了,myModule_1.default()执行,因为导出的时候就是这样导出的, 所以在导入的时候,不管你名字是啥,编译后都是myModule_1.default

  1. //编译后的index.js
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const myModule_1 = require("./myModule");
  4. console.log((0, myModule_1.sum)(1, 4));
  5. console.log(myModule_1.name);
  6. (0, myModule_1.default)();

配置编译结果为 es6时的情况

在配置编译结果为es6时,发现导出时的编译结果和原文件一模一样

  1. //原文件 myModule.ts
  2. export function sum(a:number,b:number):number{
  3. return a+b
  4. }
  5. export let name="井底的蜗牛"
  6. export default function (){
  7. console.log("Hello myModule!")
  8. }
  9. //编译后的结果
  10. export function sum(a, b) {
  11. return a + b;
  12. }
  13. export let name = "井底的蜗牛";
  14. export default function () {
  15. console.log("Hello myModule!");
  16. }

导入时,不会像commonjs一样,把默认导入的名字给替换,它还是sayHello

  1. //原文件 index.ts
  2. import sayHell,{name, sum} from "./myModule";
  3. console.log(sum(1,4))
  4. console.log(name)
  5. sayHell()
  6. //编译结果
  7. import sayHell, { name, sum } from "./myModule";
  8. console.log(sum(1, 4));
  9. console.log(name);
  10. sayHell();

总结:TS中发模块化在编译结果中

  • 如果编译结果的模块化标准是ES6:没有区别
  • 如果编译结果的模块化标准是commonjs:
    • 导出:导出的声明会变成exports的属性,默认的导出会变成exports的default属性;
    • 导入:导入时给默认导出命的名字会变成,导出对象的default属性,而不是使用导入时的名字

温馨小提示

当我们在TS中导入node的一些包时,如import fs from 'fs',他可能会报错,提示说fs没有默认导出,

  1. //原文件 index.ts
  2. import fs from 'fs'
  3. fs.readFileSync('./')

编译后的结果是长这样的,因为import fs from 'fs'这个就相当于commonjs中的默认导入,而fs模块本身导出就是module.exports={}这种形式 所以编译后就会变成一个对象的default属性,然后去default属性中找readFileSync方法,这当然没有所以报错

  1. //编译后的文件 index.js
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const fs_1 = require("fs");
  4. fs_1.default.readFileSync('./');

如何解决这种报错,有三种方法

  • 1、在我们导入时使用import {readFileSync} from 'fs'这种按需导入的方式,就不会报错了,编译的结果如下
  1. Object.defineProperty(exports, "__esModule", { value: true });
  2. const fs_1 = require("fs");
  3. (0, fs_1.readFileSync)('./');
  • 2、导入时 import * as fs from 'fs' 用这种方式进行导入,也可正常使用,当然编译结果和第一种是一样的
  1. Object.defineProperty(exports, "__esModule", { value: true });
  2. const fs = require("fs");
  3. fs.readFileSync('./');
  • 3、在tsconfig.json配置文件中加入"esModuleInterop": true,进行配置,它的作用就是启用es模块化交互非es模块导出, 然后我们还是使用import fs from 'fs',他就不报错了,然后我们看看编译结果 发现多了一个__importDefault的辅助函数
  1. var __importDefault = (this && this.__importDefault) || function (mod) {
  2. return (mod && mod.__esModule) ? mod : { "default": mod };
  3. };
  4. Object.defineProperty(exports, "__esModule", { value: true });
  5. const fs_1 = __importDefault(require("fs"));
  6. fs_1.default.readFileSync('./');

温馨提示2

如何在TS中使用commonjs?看下面例子

一般我们是这样用commonjs进行导出的

  1. //导出原文件文件,myModule.ts
  2. module.exports = {
  3. name:'井底的蜗牛',
  4. sum(a:number,b:number):number{
  5. return a+b
  6. }
  7. }

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

  1. //编译后的文件 myModule.ts
  2. module.exports = {
  3. name: '井底的蜗牛',
  4. sum(a, b) {
  5. return a + b;
  6. }
  7. };

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

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

  • 在导出的时候你不要使用 module.exports ={}的方式导出,而是使用export={}的方式进行导出,编译的结果和使用module.exports ={}的方式是一样的,不同的地方就是使用export={}有类型检查
  1. //原文件 myModule.ts
  2. export = {
  3. name:'井底的蜗牛',
  4. sum(a:number,b:number):number{
  5. return a+b
  6. }
  7. }
  8. //编译后的文件 myModule.js
  9. module.exports = {
  10. name: '井底的蜗牛',
  11. sum(a, b) {
  12. return a + b;
  13. }
  14. };
  • 导入的时候也不要使用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目录下当前文件夹有没有该文件,如果没有就一层一层网上找
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Li_阴宅/article/detail/748230
推荐阅读
相关标签
  

闽ICP备14008679号