边城

ES 提议:import()

边城 · 2017-01-15翻译 · 1900阅读 原文链接

Domenic Denicola 向 ECMAScript 提议在第 3 阶段中加入“import()”。它提供了动态加载 ECMAScript 模块的功能。这篇博文会解释这个功能。

ECMAScript 模块是静态的

ECMAScript 模块是完全静态的:你必须在编译期指定要导入或者导出什么,不能在运行时根据当时的情况修改。这样做会带来一些好处,你可以在 “探索 ES6” 中阅读到相关内容。

静态结构的导入有两种语法。看看下面的示例:

    import * as someModule from './dir/someModule.js';

首先,这个导入申明必须放在模块的顶层。这就是说你不能在 if 语句或者事件处理函数中导入模块。

第二,模块说明符'./dir/someModule.js' 是固定的;你不能在运行时候把它计算出来(比如通过函数调用之类的)。

提议引入动态导入模块的功能

关于动态加载模块提议的运算符像下面的代码那样工作:

    const moduleSpecifier = './dir/someModule.js';
    import(moduleSpecifier)
    .then(someModule => someModule.foo());

这个运算符用起来像个函数:

  • 它的参数是一个与 import 申明的模块说明符格式一样字符串。不同的是,这个参数可以通过一个结果为字符串的表达式来提供。

  • 这个“函数调用”返回一个 Promise。模块加载完成,则 Promise 完成。

尽管它很像一个函数,import() 是一个运算符:为了能确定想对于当前模块的模块说明符,它需要知道是从哪个模块调用的。普通的函数可没有办法直接找到。

应用场景

按需加载

在 Web 应用刚启动的时候,有些功能不是必须的,它们可以根据后后面的需求来加载。import() 对此很有帮助,因为它可以帮你为模块提供这样的能力。比如:

    button.addEventListener('click', event => {
        import('./dialogBox.js')
        .then(dialogBox => {
            dialogBox.open();
        })
        .catch(error => {
            /* Error handling */
        })
    });

按条件加载模块

有时候你可能想根据某些条件来加载模块。比如,在旧平台上加载 polyfill。代码会像下面这样。

    if (isLegacyPlatform()) {
        import(···)
        .then(···);
    }

计算模块描述符

比如在应用需要国际化的时候,它可以帮助你动态计算模块描述符:

    import(`messages_${getLocale()}.js`)
    .then(···);

提示

通过解构访问导出

解构对于访问模块的导出的很用:

    import('./myModule.js')
    .then(({export1, export2}) => {
        ···
    });

访问默认导出

对于默认导出,你需要知道 default 是一个关键字。把它作为一个属性,使用点号就可以访问:

    import('./myModule.js')
    .then(myModule => {
        console.log(myModule.default);
    });

但是你不能把它当作变量名来用:

    import('./myModule.js')
    .then(({default: theDefault}) => {
        console.log(theDefault);
    });

动态加载多个模块

可以通过 Promise.all() 来一次性加载多个模块:

    Promise.all([
        import('./module1.js'),
        import('./module2.js'),
        import('./module3.js'),
    ])
    .then(([module1, module2, module3]) => {
        ···
    });

异步函数和 import()

import() 返回 Promise,这意味着你可以通过 异步函数(这是 ECMAScript 2017 的内容) 来使用它,让语法更漂亮:

    async function main() {
        const myModule = await import('./myModule.js');

        const {export1, export2} = await import('./myModule.js');

        const [module1, module2, module3] =
            await Promise.all([
                import('./module1.js'),
                import('./module2.js'),
                import('./module3.js'),
            ]);
    }
    main();

在模块或者脚本的顶层,你会发现使用立即执行的箭头函数非常有用:

    (async () => {
        const myModule = await import('./myModule.js');
    })();

支持 import()

相关阅读

相关文章