esm是什么?esm是一个标准,用于将javascript程序拆分为单独的模块并按需导入它们。不同于webpack和babel,esm是javascript的标准函数,在浏览器和nodejs中都已经实现了。使用esm的好处是浏览器可以优化加载模块,比使用库更高效。esm标准通过导入导出语法实现模块变量的导入导出。esm模块的特点是模块作用域,顶层变量定义在这个作用域内,对外不可见;模块脚本自动采用严格模式;模块顶层的this关键字返回undefined;esm是在编译时加载的,也就是只有所有的imports模块都加载完才会开始执行;如果同一个模块被多次加载,它只会被执行一次。exportexport语句用于导出模块中的变量。//导出变量exportletcount=1;exportconstCONST_VAR='CONST_VAR';//导出函数exportfunctionincCount(){count+=1;}//导出类exportclassDemo{}functionadd(x){returnx+count;}//使用export导出一组变量export{count,add,//使用as重命名导出的变量addasaddCount,}//exportdefaultexportdefaultadd//合并导出其他模块的变量export{name}from'./esm_module2.js'export*from'./esm_module2.js'importimport语句用于导入其他模块的变量//导入变量import{count,incCount,CONST_VAR}from'./esm_module1.js';//重命名导入的变量asimport{addCountasrenamedAddCount}from'./esm_module1.js';//importdefaultimport{defaultasdefaultAdd}from'./esm_module1.js';从'./esm_module1导入添加。js';//创建模块对象import*asmodule1from'./esm_module1.js';export导出是值引用esm模块和commonjs模块的一个显着区别是cjs导出值得复制,而esm导出是值引用。当修改模块内部的值时,cjs无法获取到修改后的值,但是esm可以获取到修改后的值。cjs示例//cjs_module1.jsvarcount=1;functionincCount(){count+=1;}module.exports={count:count,incCount:incCount,}//cjs_demo.jsvar{count,incCount}=require('./cjs_module1.js');console.log(count);//1incCount();console.log(计数);//1esm示例//esm_module1.jsletcount=1;functionincCount(){count+=1;}export{count,incCount,}//esm_demo.jsimport{count,incCount}from'./esm_module1.js';控制台日志(计数);//1incCount();console.log(计数);//2从实现原理来看,cjs的module.exports是一个对象,在运行时注入模块。执行export语句module.exports.count=count时,对象被赋予count的key,赋值为1,之后无论模块中的count变量如何变化,都不会干扰exportmodule.exports.countesm中的{count}是一个导出count变量的只读引用,也就是说,当用户读取count时,其值指向或模块中count变量的值。可以看一下阮一峰的这篇文章:ES6入门教程在HTML中使用esm,使用script标签导入esm文件,同时设置type=module来标识这个模块为顶级模块。浏览器将esm文件视为模块文件,识别模块的import语句并加载。如果未设置type=module,浏览器会将文件视为普通脚本。当在文件中找到import语句时,会报如下错误:esmloadingmechanismesm标准并没有规定模块的加载细节,这些留给具体的环境。大致分为以下四个步骤:解析:读取模块源代码,检查语法错误;load:递归加载所有导入的模块;link:为每个加载的模块生成一个模块作用域,该模块下的所有全局声明都绑定到这个作用域,包括从其他模块导入的内容;运行时:完成所有导入的加载和链接,脚本在每个加载的模块中运行语句。当遇到全局声明时,什么也不会做(声明已经在链接阶段绑定到模块范围)。可以阅读mdn上这篇深入esm的文章:ES6InDepth:Modules动态加载模块esm的一个重要特点是在编译时加载,有利于引擎的静态分析。加载过程将先于代码的执行。但是函数中不能执行import语句或者if语句://报语法错误if(true){importaddfrom'./esm_module1.js';}es2020提案引入import()函数动态加载modules,并且可以在函数和if语句中使用。import('./esm_module1.js').then(module=>{console.log(module);})import()函数接受要加载的模块的相对路径,返回一个内容为要加载的模块对象。使用import()函数也可以根据变量动态加载模块asyncfunctiongetTemplate(templateName){lettemplate=awaitimport(`./templates/${templateName}`);console.log(template);}getTemplate("foo");getTemplate("bar");getTemplate("baz");
