Динамічний імпорт import()
import(), це функція, яка дозволяє динамічно імпортувати модуль під час виконання (runtime), а не на етапі завантаження сторінки, як звичайний import. Вона повертає Promise, що резолвиться в об'єкт з експортами модуля.
Теорія
TL;DR
import()завантажує модуль у момент виклику, а не при старті застосунку.- Повертає Promise з об'єктом
{ default, ...namedExports }. - Зменшує розмір основного бандла і пришвидшує перше завантаження.
- Збирачі виносять кожен динамічний імпорт в окремий чанк, це code splitting.
- Можна викликати будь-де: у функції, в умові, в обробнику події.
- Підтримується в усіх сучасних браузерах і в Node.js від версії 14.
Швидкий приклад
// Статичний імпорт, виконується під час завантаження модуля
import { add } from './math.js';
// Динамічний імпорт, виконується тоді, коли ми його викличемо
const math = await import('./math.js');
console.log(math.add(2, 3));Звичайний import, це статика: він завантажується одразу при старті застосунку, навіть якщо модуль не потрібен. import(), це асинхронний виклик: він завантажує модуль лише тоді, коли це справді потрібно. Завдяки цьому зменшується розмір основного бандла (разом з code splitting), пришвидшується початкове завантаження сторінки і з'являється можливість підвантажувати код на вимогу.
Синтаксис і що повертає import()
import(modulePath)
.then((module) => {
// module, це об'єкт з експортами
module.default(); // якщо експорт був default
module.someFunction(); // якщо іменований експорт
})
.catch(console.error);або з async/await:
async function loadMath() {
const math = await import('./math.js');
console.log(math.add(2, 3));
}import() повертає Promise, який:
- виконується успішно і дає об'єкт модуля
{ default, ...namedExports }; - або завершується помилкою і потрапляє в
catch(), якщо файл не знайдено або сталася помилка завантаження.
Ліниве підключення та умовні імпорти
button.addEventListener('click', async () => {
const { showModal } = await import('./modal.js');
showModal();
});Тут модуль modal.js буде завантажено лише при кліку, а не під час початкового завантаження сторінки.
Так само працює імпорт за умовою:
if (user.isAdmin) {
const admin = await import('./admin-panel.js');
admin.init();
}Модуль завантажується тільки якщо він потрібен, тобто для адміністраторів. Звичайні користувачі не тягнуть зайвий код.
Шлях можна складати з шаблонного рядка:
const moduleName = 'chart';
const module = await import(`./plugins/${moduleName}.js`);Але важливо: шлях має бути відомий збирачеві заздалегідь, інакше він може не включити файл у збірку. На практиці збирач бере статичну частину шляху (./plugins/) і додає в бандл усі файли з цієї теки.
Як це працює у збирачах
У збирачах на кшталт Webpack, Vite чи Rollup виклик import() автоматично створює окремий чанк, тобто окремий файл.
Наприклад:
import('./analytics.js');створить окремий файл analytics-XYZ.chunk.js, який завантажиться лише при виклику import(). Це і є механізм code splitting, динамічне підвантаження коду.
Відмінності від звичайного import
| Властивість | import (статичний) | import() (динамічний) |
|---|---|---|
| Синтаксис | import { x } from 'y' | await import('y') |
| Коли виконується | Під час завантаження модуля | Під час виконання, за викликом |
| Асинхронність | Синхронний на рівні збірки | Асинхронний, Promise |
| Де пишеться | Вгорі файлу | Будь-де: у функції, в умові |
| Застосування | Основний код застосунку | Ліниві модулі, code splitting |
Приклад у React
import React, { lazy, Suspense } from 'react';
const Settings = lazy(() => import('./Settings'));
export default function App() {
return (
<Suspense fallback={<p>Loading...</p>}>
<Settings />
</Suspense>
);
}Тут React використовує import() під капотом. Компонент Settings завантажується лише тоді, коли його справді відображають.
Підсумок:
| Ключова ідея | Значення |
|---|---|
| Що робить | Асинхронно завантажує модуль під час виконання |
| Що повертає | Promise, який резолвиться в об'єкт експортів |
| Для чого | Ліниве завантаження, code splitting, умовні імпорти |
| Переваги | Пришвидшує завантаження, зменшує бандл |
| Сумісність | Усі сучасні браузери та Node.js 14+ |
Типові помилки
- Забувати, що це Promise.
const m = import('./a.js'); m.add(1, 2)не спрацює, боm, це Promise, потрібніawaitабо.then(). - Брати іменований експорт як default. Об'єкт модуля містить
defaultокремим полем, тому для default-експорту потрібно(await import('./a.js')).default. - Складати шлях повністю динамічно.
await import(userInput)збирач проаналізувати не може, потрібна хоча б статична основа шляху. - Не обробляти помилку завантаження. Мережа може впасти, і без
catch()користувач отримає порожній екран замість повідомлення чи повторної спроби. - Розбивати код надто дрібно. Десятки мікрочанків дають десятки запитів, і виграш від code splitting з'їдається накладними витратами.
- Лінити те, що потрібне одразу. Якщо модуль потрібен на першому екрані, динамічний імпорт лише додасть затримку замість того, щоб її прибрати.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.