ES Module
本博主會(huì)持續(xù)更新各種前端的技術(shù),如果各位道友喜歡漾脂,可以關(guān)注假颇、收藏、點(diǎn)贊下本博主的文章骨稿。
ES Module 基本特性
- ESM 自動(dòng)采用嚴(yán)格模式笨鸡,忽略 'use strict'
- 每個(gè) ES Module 都是運(yùn)行在單獨(dú)的私有作用域中
- ESM 是通過(guò) CORS 的方式請(qǐng)求外部 JS 模塊的
- ESM 的 script 標(biāo)簽會(huì)延遲執(zhí)行腳本(瀏覽器頁(yè)面渲染后執(zhí)行)
export
在創(chuàng)建 JavaScript 模塊時(shí),export 語(yǔ)句用于從模塊中導(dǎo)出實(shí)時(shí)綁定的函數(shù)坦冠、對(duì)象或原始值形耗,以便其他程序可以通過(guò) import 語(yǔ)句使用它們。被導(dǎo)出的綁定值依然可以在本地進(jìn)行修改辙浑。在使用 import 進(jìn)行導(dǎo)入時(shí)激涤,這些綁定值只能被導(dǎo)入模塊所讀取,但在 export 導(dǎo)出模塊中對(duì)這些綁定值進(jìn)行修改判呕,所修改的值也會(huì)實(shí)時(shí)地更新倦踢。
無(wú)論您是否聲明,導(dǎo)出的模塊都處于嚴(yán)格模式佛玄。 export 語(yǔ)句不能用在嵌入式腳本中硼一。
// 導(dǎo)出單個(gè)特性
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 語(yǔ)句用于導(dǎo)入由另一個(gè)模塊導(dǎo)出的綁定。無(wú)論是否聲明了 strict mode 梦抢,導(dǎo)入的模塊都運(yùn)行在嚴(yán)格模式下般贼。在瀏覽器中,import 語(yǔ)句只能在聲明了 type="module" 的 script 的標(biāo)簽中使用奥吩。
此外哼蛆,還有一個(gè)類(lèi)似函數(shù)的動(dòng)態(tài) import(),它不需要依賴(lài) type="module" 的 script 標(biāo)簽霞赫。
在 script 標(biāo)簽中使用 nomodule 屬性腮介,可以確保向后兼容。
在您希望按照一定的條件或者按需加載模塊的時(shí)候端衰,動(dòng)態(tài) import() 是非常有用的叠洗。而靜態(tài)型的 import 是初始化加載依賴(lài)項(xiàng)的最優(yōu)選擇甘改,使用靜態(tài) import 更容易從代碼靜態(tài)分析工具和 tree shaking 中受益。
// 導(dǎo)入整個(gè)模塊的內(nèi)容
import * as myModule from '/modules/my-module.js';
// 導(dǎo)入單個(gè)接口
import {myExport} from '/modules/my-module.js';
// 導(dǎo)入多個(gè)接口
import {foo, bar} from '/modules/my-module.js';
// 導(dǎo)入帶有別名的接口
import {reallyReallyLongModuleExportName as shortName} from '/modules/my-module.js';
// 導(dǎo)入時(shí)重命名多個(gè)接口
import {
reallyReallyLongModuleMemberName as shortName,
anotherLongModuleName as short
} from '/modules/my-module.js';
// 僅為副作用而導(dǎo)入一個(gè)模塊
// 整個(gè)模塊僅為副作用(中性詞灭抑,無(wú)貶義含義)而導(dǎo)入十艾,而不導(dǎo)入模塊中的任何內(nèi)容(接口)。 這將運(yùn)行模塊中的全局代碼, 但實(shí)際上不導(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
// 動(dòng)態(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
// 第一忘嫉,將文件的擴(kuò)展名由 .js 改為 .mjs;
// 第二案腺,啟動(dòng)時(shí)需要額外添加 `--experimental-modules` 參數(shù)庆冕;
import { foo, bar } from './module.mjs';
console.log(foo, bar);
// 此時(shí)我們也可以通過(guò) 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');
// 對(duì)于第三方的 NPM 模塊也可以通過(guò) esm 加載
import _ from 'lodash';
_.camelCase('ES Module');
// 不支持劈榨,因?yàn)榈谌侥K都是導(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 始終只會(huì)導(dǎo)出一個(gè)默認(rèn)成員
- 注意 import 不是解構(gòu)導(dǎo)出對(duì)象
commonjs.js
// CommonJS 模塊始終只會(huì)導(dǎo)出一個(gè)默認(rèn)成員
// module.exports = {
// foo: 'commonjs exports value'
// }
// exports.foo = 'commonjs exports value'
// 不能在 CommonJS 模塊中通過(guò) 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)出對(duì)象
// import { foo } from './commonjs.js'
// console.log(foo)
// export const foo = 'es module export value'
與 CommonJS 的差異
esm.mjs
// ESM 中沒(méi)有模塊全局成員了
// // 加載模塊函數(shù)
// console.log(require)
// // 模塊對(duì)象
// console.log(module)
// // 導(dǎo)出對(duì)象別名
// console.log(exports)
// // 當(dāng)前文件的絕對(duì)路徑
// console.log(__filename)
// // 當(dāng)前文件所在目錄
// console.log(__dirname)
// -------------
// require, module, exports 自然是通過(guò) import 和 export 代替
// __filename 和 __dirname 通過(guò) import 對(duì)象的 meta 屬性獲取
// const currentUrl = import.meta.url
// console.log(currentUrl)
// 通過(guò) 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 之后的版本,可以通過(guò)package.json
中添加type
字段為module
鞋既,將默認(rèn)模塊系統(tǒng)修改為ES Module
力九,此時(shí)就不需要修改文件擴(kuò)展名為.mjs
了
如果需要在type=module
的情況下繼續(xù)使用CommonJS
耍铜,需要將文件擴(kuò)展名修改為.cjs
對(duì)于早期的 Node.js 版本邑闺,可以使用 Babel 實(shí)現(xiàn) ES Module 的兼容
// 配置:第一種方式
{
"plugins": [
"@babel/plugin-transform-modules-commonjs"
]
}
// 配置:第二種方式(合集)
{
"presets":["@babel/preset-env"]
}