JavaScript ES 模組:現代化前端編程必備技能
- 2020 年 11 月 12 日
- 筆記
- javascript, 前端筆記
自從 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 指定別名。導出和靜態導入必須在文件的最外層,動態導入可以在程式碼的函數中非同步完成。


