在本教程中,您将学习如何使用 ES2020 的函数 import() 动态导入模块。

JavaScript import() 简介

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);
}

并在 app.js 使用 show() 函数:

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() 工作原理如下:

  • 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 ,您可以像这样在 app.js 模块中使用 async / await

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);
        }
    })();

});

JavaScript import 实际用例

按需加载模块

应用程序启动时,某些功能可能不需要可用。为了减少加载时间,您可以将此类功能放在模块中并使用 import() 按需加载它们,如下所示:

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

根据条件加载模块

import() 语句放在  if-else 等条件语句里面,就可以根据指定的条件加载模块。以下示例加载一个针对指定平台的模块:

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

计算模块说明符

模块说明符是一个表达式,允许您决定在运行时加载哪个模块。例如,您可以根据用户的语言环境加载模块,以用户的语言展示消息:

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 关键字访问默认导出。