import

Baseline 廣泛可用 *

此功能已成熟,並可在許多裝置和瀏覽器版本上執行。自 2018 年 5 月起,它已在各個瀏覽器中可用。

* 此特性的某些部分可能存在不同級別的支援。

靜態的 import 宣告用於匯入由另一個模組匯出的只讀的即時繫結。匯入的繫結被稱為即時繫結,因為它們由匯出該繫結的模組更新,但不能被匯入模組重新賦值。

為了在原始檔中使用 import 宣告,執行時必須將該檔案解釋為模組。在 HTML 中,這是透過向 <script> 標籤新增 type="module" 來實現的。模組會自動在嚴格模式下解釋。

還有一種類似函式的動態 import(),它不需要 type="module" 指令碼。

語法

js
import defaultExport from "module-name";
import * as name from "module-name";
import { export1 } from "module-name";
import { export1 as alias1 } from "module-name";
import { default as alias } from "module-name";
import { export1, export2 } from "module-name";
import { export1, export2 as alias2, /* … */ } from "module-name";
import { "string name" as alias } from "module-name";
import defaultExport, { export1, /* … */ } from "module-name";
import defaultExport, * as name from "module-name";
import "module-name";
defaultExport

將引用模組中預設匯出的名稱。必須是有效的 JavaScript 識別符號。

module-name

要匯入的模組。只允許使用單引號和雙引號的字串字面量。指定符的評估由宿主指定。大多數宿主與瀏覽器保持一致,將指定符解析為相對於當前模組 URL 的 URL(參見 import.meta.url)。Node、打包工具和其他非瀏覽器環境通常在此基礎上定義自己的功能,因此您應該查閱它們的文件以瞭解確切的規則。模組說明符解析部分也有更多資訊。

name

模組物件的名稱,在引用匯入時將用作一種名稱空間。必須是有效的 JavaScript 識別符號。

exportN

要匯入的匯出名稱。名稱可以是識別符號或字串字面量,具體取決於 module-name 宣告要匯出的內容。如果它是字串字面量,則必須將其別名為有效的識別符號。

aliasN

將引用命名匯入的名稱。必須是有效的 JavaScript 識別符號。

"module-name" 後面可以跟一組匯入屬性,以 with 關鍵字開頭。

描述

import 宣告只能出現在模組中,並且只能在頂層(即,不能在塊、函式等內部)。如果在非模組上下文(例如,沒有 type="module"<script> 標籤、evalnew Function,它們都將“指令碼”或“函式體”作為解析目標)中遇到 import 宣告,則會丟擲 SyntaxError。要在非模組上下文中載入模組,請改用動態匯入語法。

所有匯入的繫結不能與任何其他宣告處於同一作用域,包括 letconstclassfunctionvarimport 宣告。

import 宣告被設計為語法嚴格(例如,只允許字串字面量指定符,只允許在頂層使用,所有繫結必須是識別符號),這允許模組在評估之前進行靜態分析和連結。這是使模組本質上非同步的關鍵,並支援頂層 await 等功能。

匯入宣告的形式

有四種形式的 import 宣告

以下示例旨在闡明語法。

命名匯入

給定一個名為 myExport 的值,它已從模組 my-module 隱式地作為 export * from "another.js" 或顯式地使用 export 語句匯出,這會將 myExport 插入當前作用域。

js
import { myExport } from "/modules/my-module.js";

您可以從同一個模組匯入多個名稱。

js
import { foo, bar } from "/modules/my-module.js";

您可以在匯入時重新命名匯出。例如,這會將 shortName 插入當前作用域。

js
import { reallyReallyLongModuleExportName as shortName } from "/modules/my-module.js";

模組還可以將成員作為字串字面量匯出,該字串字面量不是有效的識別符號,在這種情況下,您必須將其別名化才能在當前模組中使用。

js
// /modules/my-module.js
const a = 1;
export { a as "a-b" };
js
import { "a-b" as a } from "/modules/my-module.js";

注意: import { x, y } from "mod" 不等同於 import defaultExport from "mod" 然後從 defaultExport 中解構 xy。命名匯入和預設匯入在 JavaScript 模組中是不同的語法。

預設匯入

預設匯出需要使用相應的預設匯入語法進行匯入。此版本直接匯入預設值。

js
import myDefault from "/modules/my-module.js";

由於預設匯出沒有顯式指定名稱,因此您可以為識別符號指定任何您喜歡的名稱。

還可以使用名稱空間匯入或命名匯入來指定預設匯入。在這種情況下,預設匯入必須首先宣告。例如:

js
import myDefault, * as myModule from "/modules/my-module.js";
// myModule.default and myDefault point to the same binding

or

js
import myDefault, { foo, bar } from "/modules/my-module.js";

匯入名為 default 的名稱與預設匯入具有相同的效果。由於 default 是一個保留字,因此需要將其別名化。

js
import { default as myDefault } from "/modules/my-module.js";

名稱空間匯入

以下程式碼將 myModule 插入當前作用域,其中包含來自 /modules/my-module.js 模組的所有匯出。

js
import * as myModule from "/modules/my-module.js";

在這裡,myModule 代表一個名稱空間物件,其中包含所有匯出作為屬性。例如,如果上面匯入的模組包含匯出 doAllTheAmazingThings(),您將像這樣呼叫它:

js
myModule.doAllTheAmazingThings();

myModule 是一個 密封 物件,具有 null 原型。預設匯出以名為 default 的鍵的形式提供。有關更多資訊,請參閱模組名稱空間物件

注意: JavaScript 沒有像 import * from "module-name" 這樣的萬用字元匯入,因為名稱衝突的可能性很高。

只為副作用匯入模組

只為副作用匯入整個模組,而不匯入任何內容。這會執行模組的全域性程式碼,但實際上不匯入任何值。

js
import "/modules/my-module.js";

這通常用於polyfill,它們會修改全域性變數。

提升

匯入宣告是提升的。在這種情況下,這意味著匯入引入的識別符號在整個模組作用域中都可用,並且它們的副作用在模組的其餘程式碼執行之前產生。

js
myModule.doAllTheAmazingThings(); // myModule.doAllTheAmazingThings is imported by the next line

import * as myModule from "/modules/my-module.js";

模組指定符解析

ECMAScript 規範沒有定義模組指定符如何解析,並將其留給宿主環境(例如,瀏覽器、Node.js、Deno)。瀏覽器行為由HTML 規範指定,這已成為所有環境的事實標準

HTML 規範、Node 和許多其他規範都廣泛識別三種類型的指定符:

  • /./../ 開頭的相對指定符,它們相對於當前模組 URL 解析。
  • 可解析為 URL 的絕對指定符,它們按原樣解析。
  • 不是上述任何一種的裸指定符

相對指定符最值得注意的注意事項,特別是對於熟悉 CommonJS 約定的人來說,是瀏覽器禁止一個指定符隱式解析為許多潛在的候選。在 CommonJS 中,如果您有 main.jsutils/index.js,那麼以下所有內容都將從 utils/index.js 匯入“預設匯出”:

js
// main.js
const utils = require("./utils"); // Omit the "index.js" file name
const utils = require("./utils/index"); // Omit only the ".js" extension
const utils = require("./utils/index.js"); // The most explicit form

在 Web 上,這代價很高,因為如果您編寫 import x from "./utils",瀏覽器需要向 utilsutils/index.jsutils.js 以及可能許多其他 URL 傳送請求,直到找到可匯入的模組。因此,在 HTML 規範中,指定符預設只能是相對於當前模組 URL 解析的 URL。您不能省略副檔名或 index.js 檔名。此行為已由 Node 的 ESM 實現繼承,但它不是 ECMAScript 規範的一部分。

請注意,這並不意味著 import x from "./utils" 在 Web 上永遠不起作用。瀏覽器仍然會向該 URL 傳送請求,如果伺服器能返回正確的內容,匯入就會成功。這要求伺服器實現一些自定義解析邏輯,因為通常沒有副檔名的請求被理解為 HTML 檔案的請求。

絕對指定符可以是任何型別的URL,可解析為可匯入的原始碼。最值得注意的是:

  • HTTP URL 在 Web 上始終受支援,因為大多數指令碼已經具有 HTTP URL。Deno 本身就支援它(它最初將整個模組系統都建立在 HTTP URL 上),但它在 Node 中僅透過自定義 HTTPS 載入器獲得實驗性支援。

  • file: URL 受許多非瀏覽器執行時(如 Node)支援,因為那裡的指令碼已經具有 file: URL,但出於安全原因,瀏覽器不支援它們。

  • 資料 URL 受包括瀏覽器、Node、Deno 等在內的許多執行時支援。它們對於將小型模組直接嵌入到原始碼中非常有用。支援的 MIME 型別是那些指定可匯入原始碼的型別,例如 JavaScript 的 text/javascript、JSON 模組的 application/json、WebAssembly 模組的 application/wasm 等。(它們可能仍需要匯入屬性。)

    js
    // HTTP URLs
    import x from "https://example.com/x.js";
    // Data URLs
    import x from "data:text/javascript,export default 42;";
    // Data URLs for JSON modules
    import x from 'data:application/json,{"foo":42}' with { type: "json" };
    

    text/javascript 資料 URL 仍被解釋為模組,但它們不能使用相對匯入——因為 data: URL 方案不是分層的。也就是說,import x from "data:text/javascript,import y from './y.js';" 將會丟擲錯誤,因為相對指定符 './y.js' 無法解析。

  • node: URL 解析為內建的 Node.js 模組。它們受 Node 和其他聲稱與 Node 相容的執行時(如 Bun)支援。

裸指定符,由 CommonJS 推廣,在 node_modules 目錄中解析。例如,如果您有 import x from "foo",則執行時將在當前模組的父目錄中的任何 node_modules 目錄中查詢 foo 包。此行為可以透過使用匯入對映在瀏覽器中重現,匯入對映還允許您以其他方式自定義解析。

模組解析演算法也可以使用 HTML 規範定義的 import.meta.resolve 函式以程式設計方式執行。

示例

標準匯入

在此示例中,我們建立一個可重用模組,該模組匯出一個函式,用於獲取給定範圍內的所有素數。

js
// getPrimes.js
/**
 * Returns a list of prime numbers that are smaller than `max`.
 */
export function getPrimes(max) {
  const isPrime = Array.from({ length: max }, () => true);
  isPrime[0] = isPrime[1] = false;
  isPrime[2] = true;
  for (let i = 2; i * i < max; i++) {
    if (isPrime[i]) {
      for (let j = i ** 2; j < max; j += i) {
        isPrime[j] = false;
      }
    }
  }
  return [...isPrime.entries()]
    .filter(([, isPrime]) => isPrime)
    .map(([number]) => number);
}
js
import { getPrimes } from "/modules/getPrimes.js";

console.log(getPrimes(10)); // [2, 3, 5, 7]

匯入的值只能由匯出者修改

被匯入的識別符號是即時繫結,因為匯出它的模組可以重新賦值它,並且匯入的值會改變。但是,匯入它的模組不能重新賦值它。儘管如此,任何持有匯出物件的模組都可以修改該物件,並且所有其他匯入相同值的模組都可以觀察到修改後的值。

您還可以透過模組名稱空間物件觀察新值。

js
// my-module.js
export let myValue = 1;
setTimeout(() => {
  myValue = 2;
}, 500);
js
// main.js
import { myValue } from "/modules/my-module.js";
import * as myModule from "/modules/my-module.js";

console.log(myValue); // 1
console.log(myModule.myValue); // 1
setTimeout(() => {
  console.log(myValue); // 2; my-module has updated its value
  console.log(myModule.myValue); // 2
  myValue = 3; // TypeError: Assignment to constant variable.
  // The importing module can only read the value but can't re-assign it.
}, 1000);

規範

規範
ECMAScript® 2026 語言規範
# sec-imports

瀏覽器相容性

另見