ES Module的基本使用

2020-08-10 01:27:58

ES Module

ES Module 基本特性

  • ESM 自動採用嚴格模式,忽略 ‘use strict’
  • 每個 ES Module 都是執行在單獨的私有作用域中
  • ESM 是通過 CORS 的方式請求外部 JS 模組的
  • ESM 的 script 標籤會延遲執行指令碼(瀏覽器頁面渲染後執行)

export

在建立 JavaScript 模組時,export 語句用於從模組中導出實時系結的函數、物件或原始值,以便其他程式可以通過 import 語句使用它們。被導出的系結值依然可以在本地進行修改。在使用 import 進行匯入時,這些系結值只能被匯入模組所讀取,但在 export 導出模組中對這些系結值進行修改,所修改的值也會實時地更新。

無論您是否宣告,導出的模組都處於嚴格模式。 export 語句不能用在嵌入式指令碼中。

// 導出單個特性
export let name1, name2, …, nameN; // also var, const
export let name1 = …, name2 = …, …, nameN; // also var, const
export function FunctionName(){...}
export class ClassName {...}

// 導出列表
export { name1, name2, …, nameN };

// 重新命名導出
export { variable1 as name1, variable2 as name2, …, nameN };

// 解構導出並重新命名
export const { name1, name2: bar } = o;

// 預設導出
export default expression;
export default function (…) { … } // also class, function*
export default function name1(…) { … } // also class, function*
export { name1 as default, … };

// 合併 modules
export * from …; // does not set the default export
export * as name1 from …;
export { name1, name2, …, nameN } from …;
export { import1 as name1, import2 as name2, …, nameN } from …;
export { default } from …;

import

靜態的 import 語句用於匯入由另一個模組導出的系結。無論是否宣告瞭 strict mode ,匯入的模組都執行在嚴格模式下。在瀏覽器中,import 語句只能在宣告瞭 type=「module」 的 script 的標籤中使用。

此外,還有一個類似函數的動態 import(),它不需要依賴 type=「module」 的 script 標籤。

在 script 標籤中使用 nomodule 屬性,可以確保向後相容。

在您希望按照一定的條件或者按需載入模組的時候,動態 import() 是非常有用的。而靜態型的 import 是初始化載入依賴項的最優選擇,使用靜態 import 更容易從程式碼靜態分析工具和 tree shaking 中受益。

// 匯入整個模組的內容
import * as myModule from '/modules/my-module.js';

// 匯入單個介面
import {myExport} from '/modules/my-module.js';
// 匯入多個介面

import {foo, bar} from '/modules/my-module.js';

// 匯入帶有別名的介面
import {reallyReallyLongModuleExportName as shortName} from '/modules/my-module.js';

// 匯入時重新命名多個介面
import {
  reallyReallyLongModuleMemberName as shortName,
  anotherLongModuleName as short
} from '/modules/my-module.js';

// 僅爲副作用而匯入一個模組
// 整個模組僅爲副作用(中性詞,無貶義含義)而匯入,而不匯入模組中的任何內容(介面)。 這將執行模組中的全域性程式碼, 但實際上不匯入任何值。
import '/modules/my-module.js';

// 匯入預設值
import myDefault from '/modules/my-module.js';
import myDefault, * as myModule from '/modules/my-module.js';
// myModule used as a namespace
import myDefault, {foo, bar} from '/modules/my-module.js';
// specific, named imports

// 動態import
import('/modules/my-module.js')
  .then((module) => {
    // Do something with the module.
  });
let module = await import('/modules/my-module.js');

node 環境下

es module 使用

index.mjs

// 第一,將檔案的擴充套件名由 .js 改爲 .mjs;
// 第二,啓動時需要額外新增 `--experimental-modules` 參數;

import { foo, bar } from './module.mjs';

console.log(foo, bar);

// 此時我們也可以通過 esm 載入內建模組了
import fs from 'fs';
fs.writeFileSync('./foo.txt', 'es module working');

// 也可以直接提取模組內的成員,內建模組相容了 ESM 的提取成員方式
import { writeFileSync } from 'fs';
writeFileSync('./bar.txt', 'es module working');

// 對於第三方的 NPM 模組也可以通過 esm 載入
import _ from 'lodash';
_.camelCase('ES Module');

// 不支援,因爲第三方模組都是導出預設成員
// import { camelCase } from 'lodash'
// console.log(camelCase('ES Module'))

與 CommonJS 互動

  • ES Module 中可以匯入 CommonJS 模組
  • CommonJS 中不能匯入 ES Module 模組
  • CommonJS 始終只會導出一個預設成員
  • 注意 import 不是解構導出物件

commonjs.js

// CommonJS 模組始終只會導出一個預設成員

// module.exports = {
//   foo: 'commonjs exports value'
// }

// exports.foo = 'commonjs exports value'

// 不能在 CommonJS 模組中通過 require 載入 ES Module

// const mod = require('./es-module.mjs')
// console.log(mod)

es-module.mjs

// ES Module 中可以匯入 CommonJS 模組

// import mod from './commonjs.js'
// console.log(mod)

// 不能直接提取成員,注意 import 不是解構導出物件

// import { foo } from './commonjs.js'
// console.log(foo)

// export const foo = 'es module export value'

與 CommonJS 的差異

esm.mjs

// ESM 中沒有模組全域性成員了

// // 載入模組函數
// console.log(require)

// // 模組物件
// console.log(module)

// // 導出物件別名
// console.log(exports)

// // 當前檔案的絕對路徑
// console.log(__filename)

// // 當前檔案所在目錄
// console.log(__dirname)

// -------------

// require, module, exports 自然是通過 import 和 export 代替

// __filename 和 __dirname 通過 import 物件的 meta 屬性獲取
// const currentUrl = import.meta.url
// console.log(currentUrl)

// 通過 url 模組的 fileURLToPath 方法轉換爲路徑
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
console.log(__filename);
console.log(__dirname);

Node v12 之後的版本,可以通過package.json中新增type欄位爲module,將預設模組系統修改爲ES Module,此時就不需要修改副檔名爲.mjs

如果需要在type=module的情況下繼續使用CommonJS,需要將副檔名修改爲.cjs

對於早期的 Node.js 版本,可以使用 Babel 實現 ES Module 的相容

// 設定:第一種方式
{
  "plugins": [
    "@babel/plugin-transform-modules-commonjs"
  ]
}
// 設定:第二種方式(合集)
{
"presets":["@babel/preset-env"]
}

具體 Module 的語法