Skip to main content

Модулі в JavaScript: import/export

export віддає змінну, функцію або клас із модуля назовні, а import підключає експортовану сутність у іншому файлі. Разом вони утворюють вбудовану модульну систему, яка з'явилася в ES6 (2015) і дозволяє ділити код на файли-модулі та обмінюватися між ними значеннями, функціями, класами тощо.

Теорія

TL;DR

  • export відправляє сутність назовні, import завантажує її всередину іншого модуля.
  • Іменованих експортів у файлі може бути скільки завгодно, імпортують їх у фігурних дужках і рівно за іменем.
  • export default дозволений один на файл, імпортується без дужок і під будь-яким іменем.
  • Перейменувати можна і на експорті, і на імпорті через as, а import * as ns збирає весь модуль в один об'єкт.
  • Імпорти статичні: їх не можна писати всередині if, for чи функції; для завантаження на вимогу є await import().
  • Модуль завантажується й кешується один раз, а працює це лише в модульному контексті (type="module" в HTML або .mjs чи "type": "module" у Node.js).

Швидкий приклад

Файл math.js:

javascript
export const PI = 3.14; export function add(a, b) { return a + b; }

Файл app.js:

javascript
import { PI, add } from './math.js'; console.log(PI); // 3.14 console.log(add(2, 3)); // 5

Іменований експорт і експорт за замовчуванням

Іменований експорт (export) дозволяє віддати назовні кілька сутностей з одного файлу.

javascript
// math.js export const PI = 3.14; export function multiply(a, b) { return a * b; }

Імпортувати їх треба за іменем, у фігурних дужках, і імена мають збігатися:

javascript
import { PI, multiply } from './math.js';

Експорт за замовчуванням (export default) віддає одну головну сутність файлу, найчастіше це «головний» об'єкт, функція або клас.

javascript
// math.js export default function add(a, b) { return a + b; }

Імпортувати його можна під будь-яким іменем і без фігурних дужок:

javascript
import sum from './math.js'; console.log(sum(2, 3)); // 5

Порівняння двох форм:

Критерійexport (іменований)export default
Кількість на файлСкільки завгодноТільки один
ІмпортУ фігурних дужках {}Без фігурних дужок
Ім'яМає збігатисяМоже бути будь-яким
Перейменування при імпортіЧерез asПросто пишете інше ім'я
Прикладimport { add } from './file.js'import add from './file.js'

Три типові комбінації:

javascript
// math.js: іменований експорт export const PI = 3.14; export function add(a, b) { return a + b; }
javascript
// app.js import { PI, add } from './math.js'; console.log(add(1, 2)); // 3
javascript
// math.js: експорт за замовчуванням (функція може бути анонімною) export default function(a, b) { return a * b; }
javascript
// app.js import multiply from './math.js'; console.log(multiply(2, 3)); // 6
javascript
// math.js: змішаний експорт export const PI = 3.14; export default function add(a, b) { return a + b; }
javascript
// app.js import add, { PI } from './math.js'; console.log(add(1, 2), PI); // 3 3.14

Перейменування, імпорт усього модуля та реекспорт

Перейменувати сутність можна при експорті:

javascript
function sum(a, b) { return a + b; } export { sum as add };

і при імпорті:

javascript
import { add as plus } from './math.js'; plus(2, 3); // 5

Весь модуль можна завантажити одним об'єктом, це зручно, коли експортів багато:

javascript
import * as math from './math.js'; console.log(math.PI); // 3.14 console.log(math.add(2, 3)); // 5

Реекспорт дозволяє одному модулю «переупаковувати» інші, наприклад щоб зробити один вхідний файл для теки:

javascript
// utils.js export { add, multiply } from './math.js';

Тепер споживач бере все з одного місця:

javascript
import { add } from './utils.js';

Правила роботи модулів

ОсобливістьОпис
Працює лише в модулях (type="module" в HTML або .mjs чи "type": "module" у Node.js)Інакше буде SyntaxError
Імпорт і експорт завжди статичніЇх не можна писати всередині if, for або функції
Модулі кешуютьсяЗавантажуються один раз і далі перевикористовуються
Завантаження модулів асинхроннеДля завантаження на вимогу є await import()
Тіло модуля виконується у strict mode і має власну область видимостіЗмінні модуля не потрапляють у глобальний об'єкт

Іменовані імпорти це «живі» зв'язки (live bindings): якщо модуль пізніше змінить експортовану змінну, споживач побачить нове значення, а не копію старого.

Динамічний імпорт завантажує модуль на вимогу і повертає проміс:

javascript
const math = await import('./math.js'); console.log(math.add(2, 3)); // 5

Він працює всередині async-функції або на верхньому рівні модуля, і саме на ньому будується code splitting.

ES modules проти CommonJS

КритерійES modules (import / export)CommonJS (require / module.exports)
Синтаксисimport x from './m.js'const x = require('./m.js')
Коли резолвляться зв'язкиСтатично, до виконанняДинамічно, під час виконання
Де працюєБраузер і Node.jsІсторично Node.js
ЗавантаженняАсинхронне, є await import()require синхронний
Що дає імпортЖиві зв'язки на експортовані значенняКопію об'єкта module.exports на момент виклику
Tree shakingМожливий завдяки статичностіПрактично неможливий
javascript
// CommonJS const { add } = require('./math.js'); module.exports = { add }; // ES modules import { add } from './math.js'; export { add };

Підсумок

Ключове словоЩо робитьПриклад
exportВіддає змінні та функції назовніexport const PI = 3.14;
export defaultВіддає головну сутність файлуexport default class User {}
importПідключає експортовані сутностіimport { PI } from './math.js';

Простіше кажучи:

export це «віддати назовні», import це «завантажити всередину», а export default це «віддати одне головне».

Типові помилки

  1. Забути про модульний контекст. Звичайний <script src="app.js"> не розуміє import, потрібен <script type="module" src="app.js">, а в Node.js розширення .mjs або "type": "module" у package.json.
  2. Плутати дужки. import { add } from './math.js' бере іменований експорт, import add from './math.js' бере default. Якщо в модулі немає default, друга форма дасть undefined або помилку збирача.
  3. Помилятися в імені іменованого імпорту. Імена мають збігатися символ у символ; щоб назвати інакше, потрібен as: import { add as plus } from './math.js'.
  4. Писати кілька export default в одному файлі. Дозволений рівно один, інакше SyntaxError.
  5. Ставити import усередину умови чи функції. Статичний імпорт можливий лише на верхньому рівні модуля; для умовного завантаження є await import().
  6. Забувати розширення файлу у відносному шляху. У браузері та в Node.js з ES modules './math' не працює, треба './math.js', на відміну від звички з CommonJS і збирачів.
  7. Чекати, що модуль виконається двічі. Модулі кешуються: скільки б файлів його не імпортувало, тіло модуля відпрацює один раз.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.