第 361 篇 · 第 14 卷 前端,沒有極限 2026 年 6 月 9 日 · 台北
ls -lt ./posts --since=2013 REC · node 361 · uplink

development

完全解析 JavaScript import、export

2020 年 03 月 25 日 · 7 分鐘閱讀 · By Wang Casper

importexport 是 JavaScript 模組管理的方法,可以將每個檔案視為一個獨立的模組匯出,並在另一個檔案匯入使用。透過此方式每個檔案更能專注在特定的功能上,且能避免單一檔案過度龐大。

相容性

介紹此方法前,還是要了解一下相容性,目前來說許多主流瀏覽器都是可以運行的,但是寫法需要稍做調整,而 Node.js 則是要到 13.2 才可直接運行模組化。

瀏覽器中運行

如果要在瀏覽器運行模組化,可以在 <script> 標籤加上 type="module",接下來無論是使用行內或是外部的 JS 都能運用模組功能。

<script type="module"></script>

當然,這樣的模組功能並非所有瀏覽器都能夠運行,如果要同時兼顧新舊瀏覽器,可以改用以下做法:

<!-- 支援 module 語法的新瀏覽器 -->
<script type="module" src="module.js"></script>

<!-- 不支援 module 語法此段會被新型瀏覽器忽略 -->
<script nomodule src="IENotGood.js"></script>

Export 匯出

export 可以將函式、物件甚至是純值匯出,大部分運用上都是匯出物件(函式)為主,畢竟純值作為模組意義並不大。

export 又可區分為兩種,這兩種的匯出手法略有不同,也會影響到 import 的運用,所以在運用前請先明確區分這兩種的差異(非常重要):

  • named export(具名匯出):可匯獨立的物件、變數、函式等等,匯出前必須給予特定名稱,而匯入時也必須使用相同的名稱。另外,一個檔案中可以有多個 named export
  • default export(預設匯出):一個檔案僅能有唯一default export,而此類型不需要給予名稱。

除此之外,兩者也可共存於同一個檔案內,只不過 default export 僅能有一個。

default export(預設匯出)

default export 匯出時不需要預先賦予名稱,可以在 import 時另外賦予,但要特別注意 export default 每個檔案僅能有一個。

直接匯出純值或表達式結果。

export default 1;

預先定義一個變數並匯出,這種方式結果與上述的純值一樣。

const a = 1
export default a;

export default 匯出物件是最常見的使用方式,此方法通常也會搭配物件的縮寫形式(Object shorthand)。

const obj = { name: 'obj' };
const obj2 = { name: 'obj2' };
export default { obj, obj2 };

上段介紹的具名匯出所匯出的函式必須使用函數陳述式,而 export default 則可以直接使用匿名函式的形式匯出,就不需要預先定義。

// 匯出匿名函式
export default function() {
  console.log('這是一段函式');
}

// 匯出 class
export default class {
  constructor(name) {
    this.name = name;
  }
  callName() {
    console.log(this.name);
  }
}

named export(具名匯出)

named export 是將物件、函式等等預先賦予在特定的名稱上才能匯出,並且在 import 時也必須使用相同的名稱才能取到相同的變數或物件。

方法 1:具名匯出是需要將變數、物件預先宣告後再進行匯出,因此可以在 export 後緊接 letconst 宣告後再進行匯出。

// let, const 純值
export let a = 1;
export let obj = { name: 'obj'};

方法 2(使用率高):使用函式陳述式匯出,這種方式與上述概念是相近的,一樣是先宣告再匯出。

// 具名方法
export function fn() {
  console.log('這是一段函式')
}

方法 3(使用率高):使用物件縮寫的形式(Object shorthand)匯出物件,算是較為普遍運用的方式,可預先定義好所有的物件、方法後,在文件的結尾統一匯出。

const b = 2;
const obj2 = { name: 'obj2' };
const obj3 = { name: 'obj3' };

export { b, obj2, obj3 };

小技巧:再匯出前可另外使用 as 修改名稱。

const obj2 = { name: 'obj2' };
export { obj2 as objNewName };

並存

具名、預設匯出兩種方法可以並存在同一個檔案內,預設匯出僅能有一個。

export const obj = { name: 'obj' };
export default function() {
  console.log('這是一段函式');
}

Import 匯入

匯入的方式會與因為匯出方法不同而改變,因此必須清楚外部資源的匯出方式,如果是第三方資源則可以透過文件了解該如何匯入。

匯入 default export 並賦予名稱

預設匯出每個檔案僅會有一個,並且不會給予名稱,這種匯入方式會將預設匯出的模組引入,並且重新賦予一個變數名稱。

// export file
export default function() {
  console.log('這是一段函式');
}

// import file
import fn from './defaultModule.js';
fn(); // 直接執行函式

匯入 named export

具名的匯出方式,則需要使用解構的語法將特定的模組取出(命名需與匯出的名稱一致),並且只有被匯入的原始碼片段才能夠被執行。

// export file
export const obj = { name: 'obj'}; // 此段如果沒有被匯入,則無法運作
export function fn() {
  console.log('這是一段函式')
}

// import file
import { fn } from './module.js';
fn();

也可以透過解構同時匯入多個物件、變數、函式等等。

// import file
import { fn, obj } from './module.js';
fn();
console.log(obj)

重新命名

具名匯出的物件、變數本身就帶有固定的名稱,如果要避免與當前的作用域產生衝突,則可以使用 as(alias) 來重新命名匯入的名稱。

可以在解構時針對單一的物件、變數重新賦予名稱:

// export file
export const obj = { name: 'obj'};
export function fn() {
  console.log('這是一段函式')
}

// import file
import { fn as newFn } from './module.js';
newFn();

具名匯入亦可使用 * 來全部匯入,這時候就必須搭配 as 指向一個新的物件變數,此物件的屬性則會帶上所匯入的內容。

// import file 全部匯入並賦予至一個物件上
import * as name from './module.js';
name.fn();
console.log(name.obj);

同時匯入預設、具名

匯出時可同時存在兩種形式,因此匯入時也同樣支援。以下片段來說,在匯入時前者是帶入 default export,逗點後方則是帶入 named export

import defaultExport, * as name from "module-name";

以下程式碼分別執行了:

  1. 匯入 default export 並賦予 fn 的名稱
  2. 匯入全部的 named export,並給賦予至 named 的物件上
// export file
export const obj = { name: 'obj' };
export default function() {
  console.log('這是一段函式');
}

// import file
import fn, * as named from './defaultModule.js';
fn();
console.log(named.obj);

匯入 Side Effect 模組

有些模組並沒有實作 export,例如可直接執行的函式檔案,載入後會直接執行,不需要例外的呼叫即可作為 Side Effect 模組

常見案例如早期版本的框架,直接將方法綁定於 window,因此不需要另外呼叫即可運作,如:jQueryangularJs 就屬於此類型。

範例如下:模組檔案不需要進行匯出,直接 import 後就能運行。

// module file
(function() {
  console.log('IIFE');
})();

// import file
import './fn.js';

要用 defaultnamed

default 是非常易於使用的,針對只有單一元件、模組的檔案匯出上相對直覺很多,我自己開發上也多是利用 default export

但在針對此議題來說,也有查閱過部分的文件,許多都是提到 export default 有管理上的問題,如:

  • 具名匯入時,命名須完全一致才可匯入,增加開發的嚴謹性
  • 預設匯入時,無法確認該模組內是否有特定變數

因此在開發大型專案、開源套件、眾多模組時,大多都會推薦使用 named export 的形式,不過這並非強制性的要求,大家也可以是自己開發上的需求做選擇。

相關文件: