ES Module的基本使用

ES Module

本博主會持續(xù)更新各種前端的技術(shù),如果各位道友喜歡,可以關(guān)注、收藏、點贊下本博主的文章。

ES Module 基本特性

  • ESM 自動采用嚴(yán)格模式,忽略 'use strict'
  • 每個 ES Module 都是運行在單獨的私有作用域中
  • ESM 是通過 CORS 的方式請求外部 JS 模塊的
  • ESM 的 script 標(biāo)簽會延遲執(zhí)行腳本(瀏覽器頁面渲染后執(zhí)行)

export

在創(chuàng)建 JavaScript 模塊時,export 語句用于從模塊中導(dǎo)出實時綁定的函數(shù)、對象或原始值,以便其他程序可以通過 import 語句使用它們。被導(dǎo)出的綁定值依然可以在本地進(jìn)行修改。在使用 import 進(jìn)行導(dǎo)入時,這些綁定值只能被導(dǎo)入模塊所讀取,但在 export 導(dǎo)出模塊中對這些綁定值進(jìn)行修改,所修改的值也會實時地更新。

無論您是否聲明,導(dǎo)出的模塊都處于嚴(yán)格模式。 export 語句不能用在嵌入式腳本中。

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

// 導(dǎo)出列表
export { name1, name2, …, nameN };

// 重命名導(dǎo)出
export { variable1 as name1, variable2 as name2, …, nameN };

// 解構(gòu)導(dǎo)出并重命名
export const { name1, name2: bar } = o;

// 默認(rèn)導(dǎ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

靜態(tài)的 import 語句用于導(dǎo)入由另一個模塊導(dǎo)出的綁定。無論是否聲明了 strict mode ,導(dǎo)入的模塊都運行在嚴(yán)格模式下。在瀏覽器中,import 語句只能在聲明了 type="module" 的 script 的標(biāo)簽中使用。

此外,還有一個類似函數(shù)的動態(tài) import(),它不需要依賴 type="module" 的 script 標(biāo)簽。

在 script 標(biāo)簽中使用 nomodule 屬性,可以確保向后兼容。

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

// 導(dǎo)入整個模塊的內(nèi)容
import * as myModule from '/modules/my-module.js';

// 導(dǎo)入單個接口
import {myExport} from '/modules/my-module.js';
// 導(dǎo)入多個接口

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

// 導(dǎo)入帶有別名的接口
import {reallyReallyLongModuleExportName as shortName} from '/modules/my-module.js';

// 導(dǎo)入時重命名多個接口
import {
  reallyReallyLongModuleMemberName as shortName,
  anotherLongModuleName as short
} from '/modules/my-module.js';

// 僅為副作用而導(dǎo)入一個模塊
// 整個模塊僅為副作用(中性詞,無貶義含義)而導(dǎo)入,而不導(dǎo)入模塊中的任何內(nèi)容(接口)。 這將運行模塊中的全局代碼, 但實際上不導(dǎo)入任何值。
import '/modules/my-module.js';

// 導(dǎo)入默認(rèn)值
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

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

node 環(huán)境下

es module 使用

index.mjs

// 第一,將文件的擴展名由 .js 改為 .mjs;
// 第二,啟動時需要額外添加 `--experimental-modules` 參數(shù);

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

console.log(foo, bar);

// 此時我們也可以通過 esm 加載內(nèi)置模塊了
import fs from 'fs';
fs.writeFileSync('./foo.txt', 'es module working');

// 也可以直接提取模塊內(nèi)的成員,內(nèi)置模塊兼容了 ESM 的提取成員方式
import { writeFileSync } from 'fs';
writeFileSync('./bar.txt', 'es module working');

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

// 不支持,因為第三方模塊都是導(dǎo)出默認(rèn)成員
// import { camelCase } from 'lodash'
// console.log(camelCase('ES Module'))

與 CommonJS 交互

  • ES Module 中可以導(dǎo)入 CommonJS 模塊
  • CommonJS 中不能導(dǎo)入 ES Module 模塊
  • CommonJS 始終只會導(dǎo)出一個默認(rèn)成員
  • 注意 import 不是解構(gòu)導(dǎo)出對象

commonjs.js

// CommonJS 模塊始終只會導(dǎo)出一個默認(rèn)成員

// 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 中可以導(dǎo)入 CommonJS 模塊

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

// 不能直接提取成員,注意 import 不是解構(gòu)導(dǎo)出對象

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

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

與 CommonJS 的差異

esm.mjs

// ESM 中沒有模塊全局成員了

// // 加載模塊函數(shù)
// console.log(require)

// // 模塊對象
// console.log(module)

// // 導(dǎo)出對象別名
// console.log(exports)

// // 當(dāng)前文件的絕對路徑
// console.log(__filename)

// // 當(dāng)前文件所在目錄
// console.log(__dirname)

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

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

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

// 通過 url 模塊的 fileURLToPath 方法轉(zhuǎn)換為路徑
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,將默認(rèn)模塊系統(tǒng)修改為ES Module,此時就不需要修改文件擴展名為.mjs

如果需要在type=module的情況下繼續(xù)使用CommonJS,需要將文件擴展名修改為.cjs

對于早期的 Node.js 版本,可以使用 Babel 實現(xiàn) ES Module 的兼容

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

具體 Module 的語法

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容