關(guān)于ES模塊,本文將提供一些詳細(xì)的解讀,希望對你有所幫助!
什么是ES模塊?
ECMAScript模塊(簡稱ES模塊)是2015年推出的 JavaScript 中代碼重用的機制。在高度碎片化的 JavaScript 模塊場景中,它終于成為了標(biāo)準(zhǔn)。
在2015年之前, JavaScript 還沒有一個標(biāo)準(zhǔn)的代碼重用機制。這方面曾有過很多標(biāo)準(zhǔn)化的嘗試,導(dǎo)致這些年亂七八糟的碎片化。
你可能聽說過 AMD 模塊、UMD 或者 CommonJS。沒有明顯的贏家。終于,隨著 ECMAScript 2015,ES模塊登陸語言。
我們現(xiàn)在有了一個 "官方 "的模塊系統(tǒng)。
ECMAScript模塊無處不在?
理論上,ECMAScript 模塊應(yīng)該普遍適用于所有 JavaScript 環(huán)境。實際上,瀏覽器仍然是ES模塊的主要目標(biāo)。
2020年5月,Node.js v12.17.0 發(fā)貨時,支持 ECMAScript 模塊,沒有標(biāo)志。這意味著我們現(xiàn)在可以在 Node.js 中使用導(dǎo)入和導(dǎo)出,而無需任何額外的命令行標(biāo)志。
在 ECMAScript 模塊在任何 JavaScript 環(huán)境中普遍工作之前,還有很長的路要走,但方向是正確的。
ES模塊是怎樣的?
一個ES模塊就是一個簡單的文件,我們可以聲明一個或多個出口。以這個虛構(gòu)的 utils.js 為例。
// utils.js
export function funcA() {
return "Hello named export!";
}
export default function funcB() {
return "Hello default export!";
}
我們這里有兩個導(dǎo)出。
第一個是一個命名的導(dǎo)出,后面是一個默認(rèn)的導(dǎo)出,表示為導(dǎo)出默認(rèn)。
假設(shè)我們的項目文件夾中住著這個名為 utils.js 的文件,我們可以在另一個文件中導(dǎo)入這個模塊提供的對象。
如何從ES模塊導(dǎo)入
假設(shè)我們在項目文件夾中還有一個名為 consumer.js 的文件。要導(dǎo)入 utils.js 所暴露的函數(shù),我們可以這樣做。
// consumer.js
import { funcA } from "./util.js";
這種語法是一種命名的導(dǎo)入方式,與命名的導(dǎo)出方式有異曲同工之妙。
如果要導(dǎo)入定義為默認(rèn)導(dǎo)出的 funcB,我們可以這樣做:
// consumer.js
import funcB from "./util.js";
如果我們想在一個文件中同時導(dǎo)入默認(rèn)導(dǎo)出和命名導(dǎo)出,我們可以將其壓縮為:
// consumer.js
import funcB, { funcA } from "./util.js";
funcB();
funcA();
我們也可以用 star 導(dǎo)入整個模塊。
import * as myModule from "./util.js";
myModule.funcA();
myModule.default();
要注意,在這種情況下,必須顯式調(diào)用默認(rèn)導(dǎo)出。
要從遠程模塊導(dǎo)入。
import { createStore } from "https://unpkg.com/redux@4.0.5/es/redux.mjs";
const store = createStore(/* do stuff */)
瀏覽器中的ECMAScript模塊
現(xiàn)代瀏覽器支持 ES 模塊,盡管有一些注意事項。要加載一個模塊,請在腳本標(biāo)簽的 type 屬性中添加模塊。
ECMAScript modules in the browser
<p id="el">The result is: </p>
import { appendResult } from "./myModule.js";
const el = document.getElementById("el");
appendResult(el);
這里 myModule.js 是同一個項目文件夾下的一個簡單模塊。
export function appendResult(element) {
const result = Math.random();
element.innerText += result;
}
雖然可以直接在瀏覽器中使用ES模塊,但現(xiàn)在捆綁 JavaScript 應(yīng)用的任務(wù)仍然是 webpack 等工具的專屬,以獲得最大的靈活性、代碼拆分和對舊瀏覽器的兼容性。
動態(tài)導(dǎo)入
ES 模塊是靜態(tài)的,這意味著我們無法在運行時更改導(dǎo)入。有了2020年登陸的動態(tài)導(dǎo)入,我們可以根據(jù)用戶的交互動態(tài)加載我們的代碼(webpack在ECMAScript 2020中提供動態(tài)導(dǎo)入功能之前就已經(jīng)提供了)。
考慮一個簡單的 HTML,它可以加載一個腳本。
Dynamic imports
<button id="btn">Load!</button>
也可以考慮用幾個導(dǎo)出的 JavaScript 模塊。
// util.js
export function funcA() {
console.log("Hello named export!");
}
export default function funcB() {
console.log("Hello default export!");
}
如果要動態(tài)加載這個模塊,也許點擊一下,我們可以這樣做。
// loader.js
const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
// loads named export
import("./util.js").then(({ funcA }) => {
funcA();
});
});
在這里,我們通過重構(gòu)模塊的對象,只加載命名的導(dǎo)出。
({ funcA }) => {}
ES 模塊實際上就是 JavaScript 對象:我們可以重構(gòu)它們的屬性,也可以調(diào)用它們的任何暴露的方法。
要動態(tài)地導(dǎo)入一個默認(rèn)的導(dǎo)出,我們可以這樣做。
// loader.js
const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
// loads entire module
// runs default export
import("./util.js").then((module) => {
module.default();
});
});
當(dāng)整體導(dǎo)入一個模塊時,我們可以使用它的所有輸出。
// loader.js
const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
// loads entire module
// uses everything
import("./util.js").then((module) => {
module.funcA();
module.default();
});
});
還有一種常見的動態(tài)導(dǎo)入方式,我們在文件的頂部提取邏輯。
const loadUtil = () => import("./util.js");
const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
//
});
在這里,loadUtil 將返回一個 Promise,準(zhǔn)備進行鏈鎖。
const loadUtil = () => import("./util.js");
const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
loadUtil().then(module => {
module.funcA();
module.default();
});
});
動態(tài)導(dǎo)入看起來很好,但是它們有什么用呢?
通過動態(tài)導(dǎo)入,我們可以拆分我們的代碼,只在合適的時刻加載重要的內(nèi)容。在動態(tài)導(dǎo)入登陸JavaScript之前,這種模式是webpack這個模塊捆綁器的專屬。
像React和Vue這樣的前端庫,就大量使用了通過動態(tài)導(dǎo)入進行代碼拆分的方式,在響應(yīng)事件時加載分塊代碼,比如用戶交互或者路由變化。
JSON文件的動態(tài)導(dǎo)入
假設(shè)你在代碼庫的某個地方有一個JSON文件person.json。
{
"name": "Jules",
"age": 43
}
現(xiàn)在,你想動態(tài)地導(dǎo)入這個文件,以響應(yīng)一些用戶的交互。
由于JSON文件導(dǎo)出的只是一個默認(rèn)的導(dǎo)出,它不是一個函數(shù),所以你只能像這樣訪問默認(rèn)的導(dǎo)出。
const loadPerson = () => import("./person.json");
const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
loadPerson().then(module => {
const { name, age } = module.default;
console.log(name, age);
});
});
這里,我們從默認(rèn)的導(dǎo)出中重構(gòu)name和age。
const { name, age } = module.default;
使用async/await動態(tài)導(dǎo)入
import()語句返回的總是一個Promise,這意味著我們可以對它使用async/await。
const loadUtil = () => import("./util.js");
const btn = document.getElementById("btn");
btn.addEventListener("click", async () => {
const utilsModule = await loadUtil();
utilsModule.funcA();
utilsModule.default();
});
動態(tài)導(dǎo)入名稱
當(dāng)用import()導(dǎo)入一個模塊時,你可以隨心所欲地給它命名,只要保持一致即可。
import("./util.js").then((module) => {
module.funcA();
module.default();
});
或者:
import("./util.js").then((utilModule) => {
utilModule.funcA();
utilModule.default();
});
文章來源于公眾號:前端開發(fā)博客
以上就是W3Cschool編程獅
關(guān)于全面認(rèn)識ECMAScript模塊的相關(guān)介紹了,希望對大家有所幫助。