深入理解ES6Modules目前我们的项目几乎都是基于webpack、rollup等构建工具开发的,模块化已经成为常态。我们对它并不陌生。今天,我们将系统回顾ES6的模块机制,总结常用操作和最佳实践,希望对大家有所帮助。一些来来去去的简单背景是我们都喜欢拥有的机制。在Javascript中也是如此。将一个大型的Javascript程序分成不同的部分,使用哪个部分,就拿那个部分。很长一段时间,NodeJS都具备这种能力,后来越来越多的库和框架也具备了模块化的能力,比如CommonJS,或者基于AMD模型的实现(比如RequireJs),以及后续的Webpack、Babel等。到了2015年,一个标准的模块化系统诞生了,这就是我们今天要说的主角——ES6模型系统。乍一看,不难发现ES6模型系统与CommonJS语法非常相似。毕竟ES6模型系统来自CommonJS时代,深受CommonJS的影响。看一个简单的例子,比如在CommonJs中:(https://flaviocopes.com/commo...//file.jsmodule.exports=value;//引入valueconstvalue=require('file.js')和inInES6://const.jsexportconstvalue='xxx';import{value}from'const.js'语法很相似接下来我们主要看import和export,以及几个相关的特性,来理解ES6Modules更多方面模块化的好处模块化的好处主要有两点:1.避免全局变量污染2.有效处理依赖关系随着时代的演进,浏览器已经开始原生支持es6import和export语法。让我们看一个简单的例子://util.js导出函数addTextToBody(text){constdiv=document.createElement('div');div.textContent=文本;document.body.appendChild(div);}如果要处理事件,同样如此,看一个简单的例子:ShowMessage//showImport.jsimport{showMessage}from'/show.js'document.getElementById('test').onclick=function(){showMessage();}//显示.jsexportfunctionshowMessage(){alert("HelloWorld!")}如果你想运行这个演示,小心设置一个简单的服务:$http-server否则,你会看到一个CORS抛出。至于抛错的具体原因和其他细节,不是本文的重点。如果您有兴趣,可以阅读以下链接了解详细信息。https://jakearchibald.com/201...严格模式https://developer.mozilla.org...‘usestrict’这个语句我们都很熟悉,在es5时代也经常用到,一般在文件的顶部。这条语句的目的是禁用Javascript不友好的部分,帮助我们写出更严谨的代码。这个特性在es6语法中默认启用。如果代码中有不太严格的代码,就会报错。例如:以下是我从MDN中提取的一些在严格模式下被禁用的部分:变量不能未声明函数参数必须具有唯一名称(或被视为语法错误)和被禁止分配给只读时会抛出错误propertiesOctalnumberslike00840aresyntaxerrorsAttemptstodeleteundeletetablepropertiesthrowanerrordeletepropisasyntaxerror,而不是假设deleteglobal[prop]eval不会将新变量引入其周围的范围eval并且参数不能绑定或分配给arguments不会神奇地跟踪对方法参数参数的更改。被调用者抛出TypeError,不再是TypeError,支持。不再支持在方法调用中传递的上下文不会被“装箱”(强制)成为对象不再能够使用fn.caller和fn.arguments访问JavaScript堆栈保留字(例如受保护、静态、接口等)不能boundexportsE的几种用法S6模块只支持静态导出,只能在模块的最外层使用export,不能在条件语句,也不能在函数作用域从分类上讲,exports主要有三种:NamedExports(Zeroormoreexportspermodule)DefaultExports(Onepermodule)HybridExportsexports总览://Exportingindividualfeaturesexportletname1,name2,…,nameN;//还有var,constexportletname1=…,name2=…,…,nameN;//还有var,constexportfunctionfunctionName(){...}exportclassClassName{...}//exportlistexport{name1,name2,…,nameN};//重命名exportsexport{variable1asname1,variable2asname2,…,nameN};//使用重命名导出解构赋值exportconst{name1,name2:bar}=o;//Defaultexportsexportdefaultexpression;exportdefaultfunction(…){…}//alsoclass,function*exportdefaultfunctionname1(…){…}//也是class,function*export{name1asdefault,…};//聚合modulesexport*from…;//不设置默认exportexport*asname1from…;export{name1,name2,…,nameN}from…;export{import1asname1,import2asname2,…,nameN}from…;export{d默认}来自...;介绍一下exports的常用用法1.Namedexports(导出每个函数/变量)namedexport,这种方式导出多个函数,一般用在utils、tools、common等工具类的场景中,一个类的函数集,或者统一整个站点的变量等。只需在变量或函数前面添加export关键字。//------lib.js------exportconstsqrt=Math.sqrt;exportfunctionsquare(x){returnx*x;}exportfunctiondiag(x,y){returnsqrt(square(x)+square(y));}//-----main.js用法1------import{square,diag}from'lib';console.log(square(11));//121console.log(诊断(4,3));//5//---main.js用法2------import*aslibfrom'lib';console.log(lib.square(11));//121console.log(lib.diag(4,3));//5我们也可以直接导出一个列表,比如上面的lib.js可以改写为://------lib.js------constsqrt=Math.平方根;函数square(x){返回x*x;}函数添加(x,y){返回x+y;}export{sqrt,square,add}2.Defaultexports(导出一个默认的函数/类)这种方法比较简单,一般用于class文件,或者功能比较单一的函数文件。一个模块中只能有一个exportdefault默认输出。exportdefault和export主要有两个区别:导出时不需要知道具体的变量名,导入时不需要{}//------myFunc.js------导出默认函数(){};//------main.js------从'myFunc'导入myFunc;我的功能();导出一个类//-------MyClass.js------classMyClass{}exportdefaultMyClass;//-------Main.js------importMyClassfrom'MyClass';请注意,默认导出不需要使用{}。3.混合出口(mixedexport)混合出口,即上面第一点和第二点的组合。比较常见的,比如Lodash,都是这样组合的。//------lib.js------exportvarmyVar=...;exportletmyVar=...;exportconstMY_CONST=...;exportfunctionmyFunc(){//...}exportfunction*myGeneratorFunc(){//...}exportdefaultclassMyClass{//...}//------main.js------importMyClass,{myFunc}来自“图书馆”;另一个示例lodash示例://-------lodash.js------exportdefaultfunction(obj){//...};exportfunctioneach(obj,iterator,context){//...}export{eachasforEach};//------main.js------import_,{forEach}from'lodash';4、重新导出(aliasExport)一般情况下,export导出的变量是原文件中定义的名称,但也可以使用as关键字指定别名,一般用于简化或语义导出函数姓名。//------lib.js------exportfunctiongetUserName(){//...};exportfunctionsetName(){//...};//输出别名,在import中可以同时使用原函数名和别名export{getNameasget,//允许使用不同的名字输出两次getNameasgetNameV2,setNameasset}5.ModuleRedirects(转模块导出)有时避免导入上层模块模块太多,我们可能会以底层模块作为传递,直接导出另一个模块的内容如下://------myFunc.js------exportdefaultfunction(){...};//------lib.js------export*from'myFunc';exportfunctioneach(){...};//-------main.js------importmyFunc,{each}from'lib';export只支持最外层的静态导出,只支持导出变量、函数、类。以下用法都是错误的。`错误`export用法://直接输出变量export'Mark'的值;//没有方括号或不加默认值//当只有一个导出号时,需要加默认值,或者使用方括号varname='Mark'';exportname;//export不导出块范围内的变量function(){varname='Mark';export{name};}import的几种用法import的用法和export一一对应,而import则支持静态导入和动态导入。后期支持动态导入,兼容性较差。下面我总结一下import的基本用法:1.ImportAllthings当export有多个函数或变量时,比如文中第一点export,可以使用*as关键字导出所有函数和变量,as后跟作为模块的命名空间。//导出lib的所有函数和变量import*aslibfrom'lib';//使用lib作为命名空间来调用,类似于objectconsole.log(lib.square(11));//1212.Importasingle/multipleexportfromamodule从模块文件导入单个或多个函数。与*asnamepage方法不同,这是按需导入。下面的例子//importsquare和diag两个函数import{square,diag}from'lib';//只导入square一个函数import{square}from'lib';//导入默认模块import_from'lodash';//导入默认模块和单个函数,这个主要是为了简化单个函数的调用import_,{each}from'lodash';3.导入时重命名多个导出与导出相同,也可以使用as关键字设置别名,当两个导入类的名称相同时,可以使用as重新设置导入模块的名称,或者你可以使用as来简化名称。例如://使用as来简化函数名从“blib”导入{libasGlobalLib};4.仅为副作用而导入模块有时我们只想导入一个模块,例如样式或类库。//导入样式import'./index.less';//导入类库import'lodash';5.DynamicImports静态导入会在第一次加载时下载所有模块资源。我们在实际开发的时候,有时候需要动态导入(dynamicimport)。例如,点击某个选项卡加载一些新模块://当动态导入时,返回一个promiseimport('lodash').then((lodash)=>{//Dosomethingwithlodash.});//上面这句话其实等同于constlodash=awaitimport('lodash');es7的新用法:asyncfunctionrun(){constmyModule=awaitimport('./myModule.js');const{export1,export2}=awaitimport('./myModule.js');const[module1,module2,module3]=awaitPromise.all([import('./module1.js'),import('./module2.js'),import('./module3.js'),]);}跑步();综上所述,我总结了ES6Module的简单背景和import和export的常用用法,但这还远远不够,篇幅有限,如果想了解更多可以阅读扩展下面阅读部分(质量还不错,可以看看)。最后,关注我如果你觉得这篇内容对你很有启发,那就关注我吧~更多精彩:聊一聊ESM、Bundleless、Vite、Snowpack记得一个“无限列表”滚动优化“面试三招”代码切分(上)Cache《面试三斧》(上)《面试三斧》缓存(下)《面试三斧》HTTP(上)《面试三斧》HTTP(下)《面试三斧》This》延伸阅读:ECMAScript6modules:thefinalsyntaxJavaScriptmodulesdynamic-importNode不懂require和import,会被坑得很惨https://www.zhangxinxu.com/wordpress/2018/08/browser-native-es6-export-导入模块/