由 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:
协议,因此上述代码只能在浏览器端使用