Інструкції експорту і імпорту, які ми розглядали в попередньому розділі, називаються “статичними”. Синтаксис у них дуже простий і строгий.
По-перше, ми не можемо динамічно задавати ніякі з параметрів import
.
Шлях до модуля має бути строковим примітивом і не може бути викликом функції. Ось так працювати не буде:
import ... from getModuleName(); // Помилка, має бути рядок
По-друге, ми не можемо робити імпорт залежно від умов або в процесі виконання:
if(...) {
import ...; // Помилка, заборонено
}
{
import ...; // Помилка, ми не можемо ставити імпорт у блок
}
Усе це результат того, що мета директив import
/export
– задати кістяк структури коду. Завдяки ним вона може бути проаналізована, модулі можуть бути зібрані в один файл спеціальними інструментами, а невживані експорти видалені. Це можливо тільки завдяки тому, що все статично.
Але як ми можемо імпортувати модуль динамічно, за запитом?
Вираз import()
Вираз import(module)
завантажує модуль і повертає проміс, результатом якого стає об’єкт модуля, що містить усі його експорти.
Ми можемо використати його у будь-якому місці коду, наприклад, так:
let modulePath = prompt("Який модуль завантажити?");
import(modulePath)
.then(obj => <об’єкт модуля>)
.catch(err => <помилка завантаження, наприклад якщо немає такого модуля>)
Чи якщо усередині асинхронної функції, то можна використати let module = await import(modulePath)
.
Наприклад, якщо у нас є такий модуль say.js
:
// 📁 say.js
export function hi() {
alert(`Привіт`);
}
export function bye() {
alert(`Бувай`);
}
…То динамічний імпорт може виглядати так:
let {hi, bye} = await import('./say.js');
hi();
bye();
А якщо в say.js
вказаний типовий експорт:
// 📁 say.js
export default function() {
alert("Модуль завантажився (export default)!");
}
…То для доступу до нього нам слід узяти властивість default
об’єкту модуля:
let obj = await import('./say.js');
let say = obj.default;
// або одним рядком: let {default: say} = await import('./say.js');
say();
Ось повний приклад:
export function hi() {
alert(`Привіт`);
}
export function bye() {
alert(`Бувай`);
}
export default function() {
alert("Модуль завантажений (export default)!");
}
<!doctype html>
<script>
async function load() {
let say = await import('./say.js');
say.hi(); // Привіт!
say.bye(); // Бувай!
say.default(); // Модуль завантажений (export default)!
}
</script>
<button onclick="load()">Натисни мене</button>
Динамічний імпорт працює в звичайних скриптах, він не вимагає вказівки script type="module"
.
Хоча import()
і виглядає схожим на виклик функції, насправді це спеціальний синтаксис, так само, як, наприклад, super()
.
Тому ми не можемо скопіювати import
в іншу змінну або викликати за допомогою .call/apply
. Це не функція.