ES Module,commonjs和Typescript模組系統

tpfmryjty發表於2019-12-06

首先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 在下面 github.com/Microsoft/T…

// 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和外掛)處理可以避免不必要的執行時開銷和不確定行為。

大家有什麼看法和自己實踐心得提出來一起討論啊

相關文章