Если вы уже используете Javascript в своих проектах, вы, вероятно, используете множество операторов импорта/экспорта для разделения кода на модули.

Это то, что мы называем статическим импортом. Но что, если вы хотите импортировать модуль на лету? В этом случае вам нужно использовать декларацию динамического импорта.

Как это сделать? Давайте посмотрим вместе!

🔥Основы

На самом деле, использовать динамический импорт довольно просто. Выражение выглядит как оператор функции:

import('./path-to-my-module.js');

Это похоже на функцию, но это не так: она не наследуется от Function.prototype.

Интересная часть заключается в том, что динамический импорт возвращает промис: вам нужно обрабатывать его так же, как и для любого промиса в вашем коде.

🧑‍💻 Давайте программировать!

Я создал простой модуль с двумя именованными экспортами (константа и функция) и экспортом по умолчанию:

// 🗂 new-module.js
const USERNAME_ID_KEY = 'id';
export const DEFAULT_USERNAME = 'John';
export const getUserId = (user) => user.id;
export default USERNAME_ID_KEY;

Теперь давайте создадим новый файл для импорта нашего модуля:

// 🗂 index.js
import('./new-module.js')
  .then(module => {
    console.log(module.DEFAULT_USERNAME);
    // => 'John'
    console.log(module.getUserId({ id: 123 }));
    // => 123
    console.log(module.default);
    // => 'id'
  })
  .catch(error => console.log('Error!'));

Как видите, это довольно просто: вы используете метод Promise then для получения объекта модуля, и каждый именованный экспорт находится под свойством. Для экспорта по умолчанию он находится под ключом default.

Всегда полезно обрабатывать ошибки с помощью метода catch просто потому, что его можно добавить.

Конечно, это работает и с async/await:

async function moduleLoading() {
  const {
    DEFAULT_USERNAME,
    getUserId,
    default: defaultModule,
  } = await import('./new-module.js');

  console.log(DEFAULT_USERNAME);
  // => 'John'
  console.log(getUserId({ id: 123 }));
  // => 123
  console.log(defaultModule);
  // => 'id'
}
moduleLoading();

Обязательно укажите псевдоним для экспорта default: default — это ключевое слово в Javascript, и вы не можете использовать его для собственного именования.

📃 При использовании динамического импорта?

Я вижу два основных варианта использования для этого:

🪧 Условный импорт.
Самый очевидный способ — поставить условие под импорт огромного модуля. Поскольку вы можете вызвать import() его в любом месте вашего кода, его можно поместить в условие. Например:

if (user.role === 'author') {
  import('./author')
    .then((authorModule) => {
       console.log(authorModule.version);
    });
}

🛣 Вычисляемый путь импорта.
Еще один отличный пример использования — загрузка модуля с вычисленным путем. Вы не можете сделать это со статическим импортом:

const VEHICULES = {
  CAR: 'car',
  BOAT: 'boat',
};
const pathToUse = `./${VEHICLES.CAR}-module.js`;
import(pathToUse)
  .then((module) => {
    // ...
  })
  .catch((error) => console.log("Error!"));

🌼 Заключение

Как мы видели, этот вид импорта действительно прост в использовании и может быть очень мощным. Это звучит естественно с похожим на функцию import(), а возвращаемый Promise выглядит знакомо.

Надеюсь, вам понравился этот краткий обзор, не стесняйтесь 👏, если он показался вам интересным!