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塊中使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章