App下載

全面認(rèn)識(shí)ECMAScript模塊

猿友 2020-10-10 15:20:39 瀏覽數(shù) (3009)
反饋

關(guān)于ES模塊,本文將提供一些詳細(xì)的解讀,希望對(duì)你有所幫助!

什么是ES模塊?

ECMAScript模塊(簡(jiǎn)稱ES模塊)是2015年推出的 JavaScript 中代碼重用的機(jī)制。在高度碎片化的 JavaScript 模塊場(chǎng)景中,它終于成為了標(biāo)準(zhǔn)。

在2015年之前, JavaScript 還沒(méi)有一個(gè)標(biāo)準(zhǔn)的代碼重用機(jī)制。這方面曾有過(guò)很多標(biāo)準(zhǔn)化的嘗試,導(dǎo)致這些年亂七八糟的碎片化。

你可能聽(tīng)說(shuō)過(guò) AMD 模塊、UMD 或者 CommonJS。沒(méi)有明顯的贏家。終于,隨著 ECMAScript 2015,ES模塊登陸語(yǔ)言。

我們現(xiàn)在有了一個(gè) "官方 "的模塊系統(tǒng)。

ECMAScript模塊無(wú)處不在?

理論上,ECMAScript 模塊應(yīng)該普遍適用于所有 JavaScript 環(huán)境。實(shí)際上,瀏覽器仍然是ES模塊的主要目標(biāo)。

2020年5月,Node.js v12.17.0 發(fā)貨時(shí),支持 ECMAScript 模塊,沒(méi)有標(biāo)志。這意味著我們現(xiàn)在可以在 Node.js 中使用導(dǎo)入和導(dǎo)出,而無(wú)需任何額外的命令行標(biāo)志。

在 ECMAScript 模塊在任何 JavaScript 環(huán)境中普遍工作之前,還有很長(zhǎng)的路要走,但方向是正確的。

ES模塊是怎樣的?

一個(gè)ES模塊就是一個(gè)簡(jiǎn)單的文件,我們可以聲明一個(gè)或多個(gè)出口。以這個(gè)虛構(gòu)的 utils.js 為例。

// utils.js
export function funcA() {
  return "Hello named export!";
}


export default function funcB() {
  return "Hello default export!";
}

我們這里有兩個(gè)導(dǎo)出。

第一個(gè)是一個(gè)命名的導(dǎo)出,后面是一個(gè)默認(rèn)的導(dǎo)出,表示為導(dǎo)出默認(rèn)。

假設(shè)我們的項(xiàng)目文件夾中住著這個(gè)名為 utils.js 的文件,我們可以在另一個(gè)文件中導(dǎo)入這個(gè)模塊提供的對(duì)象。

如何從ES模塊導(dǎo)入

假設(shè)我們?cè)陧?xiàng)目文件夾中還有一個(gè)名為 consumer.js 的文件。要導(dǎo)入 utils.js 所暴露的函數(shù),我們可以這樣做。

// consumer.js
import { funcA } from "./util.js";

這種語(yǔ)法是一種命名的導(dǎo)入方式,與命名的導(dǎo)出方式有異曲同工之妙。

如果要導(dǎo)入定義為默認(rèn)導(dǎo)出的 funcB,我們可以這樣做:

// consumer.js
import funcB from "./util.js";

如果我們想在一個(gè)文件中同時(shí)導(dǎo)入默認(rèn)導(dǎo)出和命名導(dǎo)出,我們可以將其壓縮為:

// consumer.js
import funcB, { funcA } from "./util.js";


funcB();
funcA();

我們也可以用 star 導(dǎo)入整個(gè)模塊。

import * as myModule from "./util.js";


myModule.funcA();
myModule.default();

要注意,在這種情況下,必須顯式調(diào)用默認(rèn)導(dǎo)出。

要從遠(yuǎn)程模塊導(dǎo)入。

import { createStore } from "https://unpkg.com/redux@4.0.5/es/redux.mjs";


const store = createStore(/* do stuff */)

瀏覽器中的ECMAScript模塊

現(xiàn)代瀏覽器支持 ES 模塊,盡管有一些注意事項(xiàng)。要加載一個(gè)模塊,請(qǐng)?jiān)谀_本標(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 是同一個(gè)項(xiàng)目文件夾下的一個(gè)簡(jiǎn)單模塊。

export function appendResult(element) {
  const result = Math.random();
  element.innerText += result;
}

雖然可以直接在瀏覽器中使用ES模塊,但現(xiàn)在捆綁 JavaScript 應(yīng)用的任務(wù)仍然是 webpack 等工具的專屬,以獲得最大的靈活性、代碼拆分和對(duì)舊瀏覽器的兼容性。

動(dòng)態(tài)導(dǎo)入

ES 模塊是靜態(tài)的,這意味著我們無(wú)法在運(yùn)行時(shí)更改導(dǎo)入。有了2020年登陸的動(dòng)態(tài)導(dǎo)入,我們可以根據(jù)用戶的交互動(dòng)態(tài)加載我們的代碼(webpack在ECMAScript 2020中提供動(dòng)態(tài)導(dǎo)入功能之前就已經(jīng)提供了)。

考慮一個(gè)簡(jiǎn)單的 HTML,它可以加載一個(gè)腳本。








    
    Dynamic imports




<button id="btn">Load!</button>





也可以考慮用幾個(gè)導(dǎo)出的 JavaScript 模塊。

// util.js
export function funcA() {
  console.log("Hello named export!");
}


export default function funcB() {
  console.log("Hello default export!");
}

如果要?jiǎng)討B(tài)加載這個(gè)模塊,也許點(diǎn)擊一下,我們可以這樣做。

// loader.js
const btn = document.getElementById("btn");


btn.addEventListener("click", () => {
  // loads named export
  import("./util.js").then(({ funcA }) => {
    funcA();
  });
});

在這里,我們通過(guò)重構(gòu)模塊的對(duì)象,只加載命名的導(dǎo)出。

({ funcA }) => {}

ES 模塊實(shí)際上就是 JavaScript 對(duì)象:我們可以重構(gòu)它們的屬性,也可以調(diào)用它們的任何暴露的方法。

要?jiǎng)討B(tài)地導(dǎo)入一個(gè)默認(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)入一個(gè)模塊時(shí),我們可以使用它的所有輸出。

// loader.js
const btn = document.getElementById("btn");


btn.addEventListener("click", () => {
  // loads entire module
  // uses everything
  import("./util.js").then((module) => {
    module.funcA();
    module.default();
  });
});

還有一種常見(jiàn)的動(dòng)態(tài)導(dǎo)入方式,我們?cè)谖募捻敳刻崛∵壿嫛?/p>

const loadUtil = () => import("./util.js");


const btn = document.getElementById("btn");


btn.addEventListener("click", () => {
  //
});

在這里,loadUtil 將返回一個(gè) Promise,準(zhǔn)備進(jìn)行鏈鎖。

const loadUtil = () => import("./util.js");


const btn = document.getElementById("btn");


btn.addEventListener("click", () => {
  loadUtil().then(module => {
    module.funcA();
    module.default();
  });
});

動(dòng)態(tài)導(dǎo)入看起來(lái)很好,但是它們有什么用呢?

通過(guò)動(dòng)態(tài)導(dǎo)入,我們可以拆分我們的代碼,只在合適的時(shí)刻加載重要的內(nèi)容。在動(dòng)態(tài)導(dǎo)入登陸JavaScript之前,這種模式是webpack這個(gè)模塊捆綁器的專屬。

像React和Vue這樣的前端庫(kù),就大量使用了通過(guò)動(dòng)態(tài)導(dǎo)入進(jìn)行代碼拆分的方式,在響應(yīng)事件時(shí)加載分塊代碼,比如用戶交互或者路由變化。

JSON文件的動(dòng)態(tài)導(dǎo)入

假設(shè)你在代碼庫(kù)的某個(gè)地方有一個(gè)JSON文件person.json。

{
  "name": "Jules",
  "age": 43
}

現(xiàn)在,你想動(dòng)態(tài)地導(dǎo)入這個(gè)文件,以響應(yīng)一些用戶的交互。

由于JSON文件導(dǎo)出的只是一個(gè)默認(rèn)的導(dǎo)出,它不是一個(gè)函數(shù),所以你只能像這樣訪問(wèn)默認(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動(dòng)態(tài)導(dǎo)入

import()語(yǔ)句返回的總是一個(gè)Promise,這意味著我們可以對(duì)它使用async/await。

const loadUtil = () => import("./util.js");


const btn = document.getElementById("btn");


btn.addEventListener("click", async () => {
  const utilsModule = await loadUtil();
  utilsModule.funcA();
  utilsModule.default();
});

動(dòng)態(tài)導(dǎo)入名稱

當(dāng)用import()導(dǎo)入一個(gè)模塊時(shí),你可以隨心所欲地給它命名,只要保持一致即可。

  import("./util.js").then((module) => {
    module.funcA();
    module.default();
  });

或者:

  import("./util.js").then((utilModule) => {
    utilModule.funcA();
    utilModule.default();
  });

文章來(lái)源于公眾號(hào):前端開(kāi)發(fā)博客

以上就是W3Cschool編程獅關(guān)于全面認(rèn)識(shí)ECMAScript模塊的相關(guān)介紹了,希望對(duì)大家有所幫助。

0 人點(diǎn)贊