TS中的模块化

95 阅读3分钟

前端模块化标准:ES6、commonJS、amd、umd、system、esnext

模块化

相关配置

配置名称含义
module设置编译结果中使用的模块化标准
moduleResolution设置解析模块的模式
noImplicitUserStrict编译结果中不包含"use strict"
removeComments编译结果移除注释
noEmitOnError错误时不生成编译结果
esModuleInterop启用es模块交互

本文主要解决两个问题

TS中如何书写模块化语句

编译结果

TS中如何让书写模块化语句

TS中,导入和导出模块,统一使用ES6的模块化标准。

简单例子

    export function sum(a: number, b: number) {
        return a + b;
    }
    export const a: number = 1;
    export default function getTime() {
        console.log(Date.now());
    }
    import getTime, { sum, a } from './myModule';
    console.log(sum(1, 2));
    console.log(a);
    console.log(getTime());

和我们正常书写ES6模块化代码一样,有几个需要注意的点

  • 模块化文件不要使用默认导出,默认导出不能享受到TS的模块导入智能提示。
  • 引入模块化文件时,不要加后缀名.ts,会报错,编译结果中不存在ts文件。

编译结果中的模块化

可以在配置文件中进行配置模块化标准

TS中的模块化在编译结果中:

  • 如果编译结果的模块化标准是ES6:没有区别

    配置文件中模块化配置为:"module": "ES6",编译结果和ts代码相同

        //模块化文件
        export function sum(a, b) {
            return a + b;
        }
        export const a = 1;
        export default function getTime() {
            console.log(Date.now());
        }
    
        //引用模块化文件
        import getTime, { sum, a } from './myModule';
        console.log(sum(1, 2));
        console.log(a);
        console.log(getTime());
    
  • 如果编译结果中的模块化标准是commomjs:导出的声明会变成exports的属性,默认的导出会变成exports的default属性。

    配置文件中模块化配置为:"module": "CommonJS",编译结果

       //模块化文件
       "use strict";
       Object.defineProperty(exports, "__esModule", { value: true });
       exports.a = exports.sum = void 0;
       function sum(a, b) {
           return a + b;
       }
       exports.sum = sum;
       exports.a = 1;
       function getTime() {
           console.log(Date.now());
       }
       exports.default = getTime;
    
       
       //引用模块化文件
       "use strict";
       Object.defineProperty(exports, "__esModule", { value: true });
       const myModule_1 = require("./myModule");
       console.log((0, myModule_1.sum)(1, 2));
       console.log(myModule_1.a);
       console.log((0, myModule_1.default)());
    

    可以使用"noImplicitUseStrict": false去掉编译结果中的"use strict",ts本身就很严格了。

    很奇怪的是,模块化文件编译结果中的void 0 console.log((0, myModule_1.sum)(1, 2))

    void是一元表达式,void 运算符对给定的表达式进行求值,然后返回undefined

        void 0 === void0) === undefined
    

    为什么这样写,简单来说就是undefined是全局对象window的属性,低版本浏览器中可以被修改

    逗号运算符是二元运算符,它能够先执行运算符左侧的操作数,然后再执行右侧的操作数,最后返回右侧操作数的值, console.log((0, myModule_1.sum)(1, 2))实际上就是console.log(myModule_1.sum)(1, 2))

解决默认导入的错误

在ts中想要使用fs模块,正常写法应该是这样

    import fs from 'fs';   //报错,fs没有默认导出
    fs.readFileSync('./');

fs模块是module.exports = {}导出的,前面的例子说明ts中使用ES6默认导出,编辑结果中是exports.default,所以导致报错

解决办法

  • 使用import { readFileSync } from 'fs';

  • 使用import * as fs from 'fs';

  • 配置参数"esModuleInterop": true

    引用模块的编译结果为

        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('./');
    

如何在typescript中书写CommonJS(不建议使用)

导出:export = xxx,才会具有类型检查,会有智能提示

导入:import xxx = require("xxx")import xxx from "xxx"

不建议使用commonJS模块化代码,除非有不可描述的原因。

模块解析

模块解析:应该从什么位置寻找模块

TS中,有两种模块解析策略

  • classic:经典(老版本)
  • node:node解析策略(唯一的变化,是将js替换为ts)
    • 相对路径require("./xxx")

      先找当前目录有没有该文件,如果没有,再找package.json文件中是否配置"main": "xxx",如果有则在当前目录下找该文件,如果没有,则找当前目录下有有没有index.ts文件

    • 非相对路径require("xxx")

      在当前目录找node_modules目录,没有则继续在上级找,直到找到

练习

模块化实现扑克牌例子github