ES提案:import() 可能会有你不知道的?

760 阅读2分钟

由 Domenic Denicola发起的提案import()相信很多人都已经在使用了,它在今年六月的tc39会议中被正式收录到Ecma草案中。如果你已经掌握了它的基础用法,可以跳过前面的内容,看下最后是不是还有你不知道的黑魔法?

为什么要使用import()

现有的import语法是静态声明,它会被 JavaScript 引擎静态分析并优先执行。这本身是个很好的设计,通过静态分析,可以提高打包工具的编译效率,还可以实现tree shaking。但终究还是会有需要动态加载模块的场景,例如一些非主流程的模块,出于性能优化考虑,我们希望在使用时再做懒加载,或是模块路径是个变量,这时候import就无法满足了。因此我们需要import()。当然,当我们使用import()的同时,我们也舍弃了import静态分析的优势,所以需要分场景按需使用。

如何使用

import()使用很容易,你只需要将模块路径传参即可。返回值是个Promise对象

import('yourModule.js').then(module => {
    // 你可以在这儿通过module使用模块内得方法
    module.api()
}).catch(err => {
    // 加载失败
})

// or
async () => {
    try{
        const module = await import('yourModule.js')
        
        // 你可以通过结构获取
        const {export1, export2} = await import('yourModule.js')
        
        // 访问default
        module.default
    } catch(err) {
        // 加载异常处理
    }
}

由于返回值是Promise对象,当你需要动态加载多个模块时,可以使用Promise.all

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

使用import()替代eval()

没错,我们还可以使用import()来实现eval()的功能,而且由于eval()并不支持export/import,import()能做到的比eval()更多。

const jsCode = `console.log('test');`;
const encodedJsCode = encodeURIComponent(jsCode);
const dataUri = `data:text/javascript;charset=utf-8,${encodedJsCode}`;
import(dataUri); // test

// export/import
const jsCode = `export default 'test export'`;
const encodedJsCode = encodeURIComponent(jsCode);
const dataUri = `data:text/javascript;charset=utf-8,${encodedJsCode}`;
import(dataUri)
  .then((module) => {
    console.log(module.default);
  }); // test export

在上述例子中,我们通过data URLs来嵌入js代码,并通过import()加载执行。在平时我们可能在小图片base64场景中使用data URLs已经见得很多了,但忽视了它还能用于执行js。

由于nodejs并不支持data:协议,因此上述代码只能在浏览器端使用