JavaScript ES 模組:現代化前端程式設計必備技能

前端小蜜蜂發表於2020-11-12

自從 ES 模組被新增到規範中後,JavaScript 中的模組就更加簡單了。模組按檔案分開,非同步載入。匯出是用 export 關鍵字定義的;值可以用 import 關鍵字匯入。

雖然匯入和匯出單個值的基礎知識非常容易掌握和使用,但還有許多其他方法可以使用 ES 模組來使你的匯入和匯出按照你需要的方式工作。在本文中,我將介紹你可以在模組中匯出和匯入的所有方法。

需要記住的一點是,匯出和靜態匯入只能發生在模組的最外層。你不能從函式、if 語句或任何其他塊中匯出或靜態匯入。另外,動態匯入可以在函式中完成,我們將在本文最後討論它。

匯出

預設匯出

每個模組都有一個 "預設 "匯出,它代表了模組匯出的主要值。可能會有更多的匯出值,但預設匯出值代表模組的定義。一個模組中只能有一個預設匯出。

const fruitBasket = new FruitBasket()

export default fruitBasket

注意,在預設匯出之前,我必須先定義該值。如果我想,我也可以立即匯出我的值,而不把它分配給一個變數,但這樣我就不能在匯出的同時將其賦值給一個變數。

我們可以預設匯出一個函式宣告和一個類宣告,而不需要先把它分配給一個變數。

export default function addToFruitBasket(fruit) {
  // ...
}

我們甚至可以將字面值作為預設匯出。

export default 123

命名匯出

任何變數宣告都可以在建立時匯出,這將建立一個 "命名匯出",使用變數名作為匯出名。

export const fruitBasket = new FruitBasket()

我們還可以立即匯出函式和類的宣告。

export function addToFruitBasket(fruit) {
  // ...
}
export class FruitBasket {
  // ...
}

如果我們想匯出一個已經定義好的變數,我們可以通過大括號把變數名包裝為物件來實現。

const fruitBasket = new FruitBasket()

export { fruitBasket }

我們甚至可以使用 as 關鍵字來重新命名我們的匯出,使之與變數名不同。如果需要,我們還可以同時匯出其他變數。

const fruitBasket = new FruitBasket()
class Apple {}

export { fruitBasket as basketOfFruit, Apple }

聚合匯出

我們還會經常遇到這種情況,就是從一個模組匯入模組,然後立即匯出這些值。比如這樣:

import fruitBasket from './fruitBasket.js'

export { fruitBasket }

當你要同時匯入和匯出很多東西時,這可能會變得很繁瑣。ES 模組允許我們同時匯入和匯出多個值。

export * from './fruitBasket.js'

這將把 ./fruitBasket.js 中所有命名匯出重新包裝在一起再匯出,但它不會匯出預設匯出值,因為一個模組只能有一個預設匯出值。如果我們要匯入和匯出多個具有預設匯出的模組,哪個值將成為匯出模組的預設匯出值呢?

我們可以專門從其他檔案中匯出預設模組,或者在重新匯出時為預設匯出命名。

export { default } from './fruitBasket.js'

// 或者

export { default as fruitBasket } from './fruitBasket.js'

我們也可以有選擇地從另一個模組匯出不同的專案,而不是把所有的專案都重新匯出。在這種情況下,我們也使用大括號。

export { fruitBasket as basketOfFruit, Apple } from './fruitBasket.js'

最後,我們可以使用 as 關鍵字將整個模組打包成一個單獨的命名匯出。假設我們有以下檔案:

// fruits.js
export class Apple {}
export class Banana {}

現在我們可以將其打包成一個單獨的匯出物件,這個物件包含了所有命名匯出和預設匯出物件。

export * as fruits from './fruits.js'
// { Apple: class Apple, Banana: class Banana }

匯入

預設匯入

當匯入一個預設值時,我們需要給它指定一個名字。既然是預設值,我們給它取什麼名字並不重要。

import fruitBasketList from './fruitBasket.js'

我們也可以同時匯入所有的匯出,包括命名匯出和預設匯出。這將會把所有的匯出放到一個物件中,而預設匯出將被賦予 "default "的屬性名。

import * as fruitBasket from './fruitBasket.js'
// { default: fruitBasket }

命名匯入

我們可以通過用大括號包裝匯出的名稱來匯入任何命名匯出。

import { fruitBasket, Apple } from './fruitBasket.js'

我們也可以在匯入時使用 as 關鍵字重新命名匯入。

import {fruitBasket as basketOfFruit, Apple} from './fruitBasket.js'

我們也可以在同一個匯入語句中混合命名匯出和預設匯出。預設匯出的內容會先列出,然後是大括號內的命名匯出內容。

import fruitBasket, { Apple } from './fruitBasket.js'

副作用匯入

有時候一個模組並沒有匯出值,我們只希望把該模組匯入進來立即執行。匯入這樣的一個模組時,不需要在檔案中列出任何匯出值。這被稱為”副作用(side-effect)“匯入,它將直接執行模組中的程式碼而不提供任何匯出值。

import './fruitBasket.js'

動態匯入

有時我們在匯入檔案之前並不知道檔案的名稱。或者我們在執行程式碼到一半的時候才需要匯入一個檔案,我們可以使用動態匯入在程式碼中的任何位置匯入模組。之所以稱之為 "動態",是因為匯入的路徑可能是不確定的,可以是字串變數也可以是字串表示式,而不像靜態匯入那樣必須是一個字串字面量。

由於 ES 模組是非同步的,所以模組不會立即可用。我們必須等待它被載入後才能對它做事情。正因為如此,動態匯入會返回一個解析模組的 Promise。

async function createFruit(fruitName) {
  try {
    const FruitClass = await import(`./${fruitName}.js`)
  } catch {
    console.error('Error getting fruit class module:', fruitName)
  }
  return new FruitClass()
}

總結

ES 匯出的內容可以是值(包括變數和字面量)也可以是類和函式的宣告,從匯出方式上可以分為預設匯出、命名匯出和聚合匯出。根據不同的匯出方式,匯入可以分為預設匯入、命名匯入、副作用匯入和動態匯入。命名匯出和匯入均可以使用 as 指定別名。​匯出和靜態匯入必須在檔案的最外層,動態匯入可以在程式碼的函式中非同步完成。

相關文章