zoukankan      html  css  js  c++  java
  • ES Module,commonjs和Typescript模块系统

    首先es和cmj大家都知道不一样,具体区别也就不多说,需要注意的是TS的模块也是自己实现的,不过在ES Module 2015(es6)定稿下来后,TS沿用和支持了ES module,不过TS除了使用ES module的规范,自己也有一套namespace的模块管理,这在某个版本前一直用namespace管理模块,直到后来和ESM保持一致才有改动。例如下面官网说法:

    TypeScript 1.5里术语名已经发生了变化。 “内部模块”现在称做“命名空间”。 “外部模块”现在则简称为“模块”,这是为了与 ECMAScript 2015里的术语保持一致,(也就是说 module X { 相当于现在推荐的写法 namespace X {)。

    注意babel是典型的ES module写法,babel本身就是将ES6,ES7的高级语法转为浏览器能读取的代码,babel项目迁移到ts,才会出现一些模块写法不兼容的问题。(react+ts项目中代码的转义是靠webpack的babel-loader来转义tsx,ts语法,babel-loader真强大,TS语法,类型的提示和校验则是靠ForkTsCheckerWebpackPlugin,TsconfigPathsPlugin,eslint等插件去完成)

    TS中常常用import * as React 导入。

    TS 把 CJS 模块作为一个 Namespace 导入,所以,为了解决上面提到的报错,需要这样导入 CJS 模块,以及任何没有 default 导出的模块: import * as React from 'react'

    这样子的代码,如果从 babel 迁移到 TS 就需要大幅的改动代码,不过 TS 也注意到了这个问题,添加了一个 compile option 支持 babel 的这种写法 esModuleInterop, PR 在下面 https://github.com/Microsoft/TypeScript/pull/19675

    // tsconfig.json
    {
    "esModuleInterop": true
    }

    在该选项中,所有 ES Module 文件会导出一个名为 __esModule 的隐藏属性,值为 true。对于导入 ES Module 时,所有行为没有变化。

    整体导入其它模块(import * as ns)时,若不是 ES Module,则将导出内容本身作为模块的 default 属性,并将其它所有属性原样拷贝到模块中。

    // module.js
    module.exports = () => {
      console.log('foo')
    }
    exports.xixi = 'xixi'
    
    // 使用
    import * obj from 'module.js';
    

    即module.export导出的模块,和其他挂载在exports上的属性,都会拷贝到* as obj的obj这个对象寄来,且执行obj.foo === 'foo' 为true,obj.default()则为打印foo,此时和es module的import 就表现一致了,导入的模块对象中default key为es module中default导出的或者cmj module.export导出的,其他属性则为es module 正常export的和cmj中挂载在exports的属性。

    而默认导入模块(import name)时,既然普通的module.export导出的内容视为default,那么TS导入就也是import xxx(随便起) from 'xxxx';

    但是注意使用babel的项目,比如TS+react,使用的bable-loader,我发现上面的代码obj只会打印出module.export导出的属性,即只打印() => {
    console.log('foo')
    }这个函数属性,xixi的属性消失,并不是ts这样打印{default:()=>xxx,xixi:"xixi"}。这里是用babel-loader处理cmj模块的表现。并不是ts内置模块系统tsc编译的做法?(待确定研究)

    这里只对ts内置的模块转换做研究,后续有时间可以再对bable的多模块系统交互转换做点研究和实践。

    TS中相应的源码处理工具函数为:

    export function __importStar(mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
        result.default = mod;
        return result;
    }
    
    export function __importDefault(mod) {
        return (mod && mod.__esModule) ? mod : { default: mod };
    }
    

    TypeScript 改善了 ES Module 与 CommonJS 的交互支持,但在实际项目还是用的比较少,tsc命令只有在node+ts项目中可能遇到,而且这种项目大家也有用ts-node来运行的,大多数项目,现在的主流ts+react,ts+vue等前端工程化项目,都是在可能的情况下使用 JavaScript 打包工具(webpack等中的一些loader和插件)处理可以避免不必要的运行时开销和不确定行为。

    ES6的import函数

    es6新增了动态引入模块的import()函数,传参和import关键字一样,只是是在运行时确定,可以再if语句中使用(import关键字不可以,因为在编译时(会被 JavaScript 引擎静态分析)就会先于其他代码执行,所以放在代码块中无意义,只能放在模块的顶层,而来就加入了import()函数,类似node的require函数,都是运行时确定,即什么时候运行到这一句,也会加载指定的模块。另外,import()函数与所加载的模块没有静态连接关系,这点也是与import语句不相同。(import关键字有时候也叫"连接")。

    import()就很像Node 的require方法,区别主要是前者是异步加载,后者是同步加载。

    import返回的是promise对象,即也可在await块中使用。

  • 相关阅读:
    LeetCode 242. Valid Anagram (验证变位词)
    LeetCode 205. Isomorphic Strings (同构字符串)
    LeetCode 204. Count Primes (质数的个数)
    LeetCode 202. Happy Number (快乐数字)
    LeetCode 170. Two Sum III
    LeetCode 136. Single Number (落单的数)
    LeetCode 697. Degree of an Array (数组的度)
    LeetCode 695. Max Area of Island (岛的最大区域)
    Spark中的键值对操作
    各种排序算法总结
  • 原文地址:https://www.cnblogs.com/zhangmingzhao/p/11995379.html
Copyright © 2011-2022 走看看