Модуль import() в JavaScript

В этом руководстве вы узнаете, как динамически импортировать модули с помощью функционального аналога import() в ES2020 JavaScript.

В ES6 представлена концепция модулей, позволяющая разрабатывать модульный код JavaScript. Предположим, у вас есть следующий простой HTML-документ с одной кнопкой:

<!DOCTYPE html>
<html>

<head>
    <title>Module Dynamic Import</title>
</head>
<body>

    <button id="show">Show Dialog</button>
    <script type="module" src="js/app.js"></script>
</body>
</html>

Когда пользователи нажимают кнопку, вы хотите показать диалоговое окно. Чтобы сделать код более организованным, вы разрабатываете модуль с именем dialog.js :

export function show(message) {
    alert(message);
}

И используете функцию show() в app.js :

import {show} from './dialog.js';

let btn = document.querySelector('#show');

btn.addEventListener('click', function () {
    show('Hi');
});

До ES2020 было невозможно динамически загружать модуль dialog.js при необходимости. Следующий пример вызовет ошибку:

let btn = document.querySelector('#show');

btn.addEventListener('click', function () {
    import {show} from './dialog.js';
    show('Hi');
});

Приведенный выше код пытается загрузить модуль dialog.js только при нажатии кнопки.

ES2020 представил динамический импорт модуля с помощью функционального метода import() со следующим синтаксисом:

import(moduleSpecifier);

import() позволяет вам динамически импортировать модуль, когда это необходимо. Вот как работает функция:

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

Чтобы динамически загрузить dialog.js, вы можете использовать import() следующим образом:

let btn = document.querySelector('#show');

btn.addEventListener('click', function() {
    import('./dialog.js')
        .then(( dialog ) => {
            dialog.show();
        })
        .catch( error => {
            // handle error here
        });
});

Поскольку import() возвращает Promise, вы можете использовать async/await в модуле app.js следующим образом:

let btn = document.querySelector('#show');

btn.addEventListener('click', function () {
    (async () => {
        try {
            let dialog = await import('./dialog.js');
            dialog.show('Hi')
        } catch (error) {
            console.log(error);
        }
    })();

});

Практические примеры

import() имеет следующие практические варианты использования:

1) Загрузка модуля по запросу

Некоторые функции могут быть недоступны при запуске приложений. Чтобы уменьшить время загрузки, вы можете разместить такие функции в модулях и использовать import() для их загрузки по запросу, например:

function eventHandler() {
    import('./module1.js')
        .then((ns) => {
            // use the module 
            ns.func();
        })
        .catch((error) => {
            // handle error
        });
}

2) Загрузка модулей в зависимости от условий

При размещении import() внутри условного оператора, такого как if-else, вы можете загружать модули на основе определенного условия. В следующем примере загружается модуль, ориентированный на определенную платформу:

if( isSpecificPlatform() ) {
    import('./platform.js')
    .then((ns) => {
        ns=>f();
    });
}

3) Спецификаторы вычисляемого модуля

Спецификатор модуля — это выражение, которое позволяет вам решить, какой модуль загружать во время выполнения.

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

let lang = `message_${getUserLocale()}.js`;

import(lang)
    .then(...);

Подробнее о функции

Использование деструктуризации объекта

Если модуль имеет несколько экспортов, вы можете использовать деструктуризацию объекта для получения экспортируемых объектов. Предположим, что dialog.js имеет две функции:

export function show(message) {
    alert(message);

}

export function hide(message) {
    console.log('Hide it...');
}

В app.js вы можете использовать деструктуризацию объекта следующим образом:

let btn = document.querySelector('#show');

btn.addEventListener('click', function () {
    (async () => {
        try {
            // use object destructuring
            let {
                show,
                hide
            } = await import('./dialog.js');

            // use the functions
            show('Hi');
            hide();
        } catch (err) {
            console.log(err);
        }
    })();

});

Динамическая загрузка нескольких модулей

Для динамической загрузки нескольких модулей вы можете использовать метод Promise.all() :

Promise.all([
    import(module1), 
    import(module2),
     ...])
    .then(([module1,module2,module3]) => {
        // use the modules
    });;

Доступ к экспорту по умолчанию

Если у модуля есть экспорт по умолчанию, вы можете получить к нему доступ, используя ключевое слово  default. Например:

import(moduleSpecifier)
    .then((module) => {
        // access the default export
        console.log(module.default);
    });

Заключение

  • Используйте JavaScript import() для динамической загрузки модуля. import() возвращает Promise, которое будет выполнено после полной загрузки модуля.
  • Используйте async / await для обработки результата import().
  • Метод Promise.all()  — для одновременной загрузки нескольких модулей.
  • Чтобы назначить переменные экспортирующим объектам модуля, используйте деструктуризацию объекта.
  • Ключевое слово default обеспечивает доступ к экспорту по умолчанию.
Рейтинг
( Пока оценок нет )
Александр Русаков / автор статьи
Программист, разработчик, 12 лет опыта работы в крупных компаниях. Быстро освоил typescript, делюсь своими знаниями на страницах этого сайта.
Загрузка ...
JavaScript и TypeScript