简介很久以前,js只是作为浏览器的交互操作存在,一般都是很短的脚本,所以独立存在。但是随着现代浏览器的发展,尤其是nodejs的出现,js能做的事情变得越来越复杂。所以我们需要一个模块系统来组织不同用途的脚本,进行逻辑上的区分和引用。今天给大家介绍一下js中的模块系统。CommonJS和NodejsCommonJS是Mozilla在2009年1月提出的,没错,就是firfox公司。原名是ServerJS。2009年8月更名为CommonJS,以表达该标准的普适性。CommonJS的主要应用是服务端的nodejs。浏览器端不直接支持CommonJS,如果要在浏览器端使用,需要进行转换。CommonJS使用require()导入模块,使用module.exports导出模块。让我们看一个CommonJS的例子:require("module");要求(“../file.js”);exports.doStuff=function(){};module.exports=someValue;请注意,CommonJS是同步加载的。AMD异步模块加载AMD的全称是AsynchronousModuleDefinition。它提供了一种异步加载模块的模式。AMD是RequireJS推广过程中模块定义的标准化输出。异步加载的好处是可以在模块需要使用的时候加载,从而减少一次性加载的时间,尤其是在浏览器端,可以提高用户体验。查看AMD加载模块的定义:define(id?,dependencies?,factory);AMD通过define来定义和加载依赖模块。其中id表示要定义的模块名称,dependencies表示这个模块依赖的模块,factory是用来初始化模块或对象的函数。让我们看一个例子://或者:returnrequire("beta").verb();}});在这个例子中,我们定义了一个alpha模块,它需要依赖三个模块“require”、“exports”和“beta”。并且在工厂中导出了beta模块的verb方法。define中既不需要id也不需要依赖项://noiddefine(["alpha"],function(alpha){return{verb:function(){returnalpha.verb()+2;}};});//没有依赖define({add:function(x,y){returnx+y;}});甚至我们可以在AMD中使用CommonJS:define(function(require,exports,module){vara=require('a'),b=require('b');exports.action=function(){};});定义后AMD使用require加载模块:require([dependencies],function(){});第一个参数是依赖模块,第二个参数是回调函数,会在前面所有的依赖之后调用模块被加载。加载的模块作为参数传递给此函数,以便它们可以在回调函数中使用。require(["模块","../文件"],函数(模块,文件){/*...*/});requireload模块是异步加载的,但是后续的回调函数只会在所有模块都加载完之后运行。CMDCMD是SeaJS推广过程中模块定义的标准化输出。它的全称是CommonModuleDefinition。CMD也使用define来定义模块。CMD把一个文件看成一个模块:define(id?,deps?,factory)看起来和AMD的define很像,有id,依赖模块和工厂。这里的factory是一个函数,有三个参数,function(require,exports,module),我们可以使用require加载工厂中要使用的模块,使用exports导出对外暴露的模块,module代表当前模块。让我们看一个例子://定义模块myModule.jsdefine(function(require,exports,module){var$=require('jquery.js')$('div').addClass('active');});//加载模块seajs.use(['myModule.js'],function(my){});所以总结一下AMD和CMD的区别就是AMD前面要加载的依赖模块必须在定义模块的时候声明它所依赖的模块。CMD加载依赖模块后,并不执行它,只是下载它,只在使用时使用require来执行。ES模块和现代浏览器ES6和现代浏览器通过导入和导出支持模块化。先看浏览器对import和export的支持:首先,我们看看如何使用export来导出要暴露的变量或方法:exportconstname='square';导出函数draw(ctx,length,x,y,color){ctx.fillStyle=color;ctx.fillRect(x,y,长度,长度);return{length:length,x:x,y:y,color:color};}基本上,我们可以使用export来导出var、let、const变量或函数甚至类。前提是这些变量或函数在顶层。更简单的方法是将所有要导出的项目放在一行中:export{name,draw,reportArea,reportPerimeter};实际上有两种导出方式,named和default。上面示例中的导出是命名格式,因为每个都有自己的名称。让我们看看如何使用export来导出默认值://exportfeaturedeclaredearlierasdefaultexport{myFunctionasdefault};//exportindividualfeaturesasdefaultexportdefaultfunction(){...}exportdefaultclass{..}named可以导出多个对象,default只能导出一个对象。导出后,我们可以使用导入导入:import{name,draw,reportArea,reportPerimeter}from'./modules/square.js';如果我们在导出时选择默认,那么我们可以在导入时使用它任何名称://文件test.jsletk;导出默认k=12;//一些其他文件importmfrom'./test';//因为默认是export,所以这里我们可以使用importmimportconsole.log(m);//willlog12我们可以在一个模块中使用import和export从不同的模块导入,然后在同一个模块中导出,这样第三方程序只需要导入这个模块即可。export{defaultasfunction1,function2}from'bar.js';上面的exportfrom等价于:import{defaultasfunction1,function2}from'bar.js';导出{函数1,函数2};在上面的例子中,我们需要分别导入function1和function2才可以使用。实际上,我们可以使用以下方法将所有导入作为Module对象的属性:import*asModulefrom'./modules/module.js';Module.function1()Module.function2()然后是function1和function2成为Module的属性,可以直接使用。在HTML中使用模块及注意事项如何在HTML中引入模块?我们有两种方法,第一种是使用src选项:第二种是将模块的内容直接放在script标签中。请注意,这两种类型的脚本标签都是模块。使用脚本加载模块时,默认是defer,所以不需要显示和添加defer属性。如果测试时使用file://加载本地文件,由于JS模块安全要求,很可能会出现CORS错误。最后,import()也可以用作动态加载模块的函数:squareBtn.addEventListener('click',()=>{import('./modules/square.js').then((Module)=>{letsquare1=newModule.Square(myCanvas.ctx,myCanvas.listId,50,50,100,'blue');square1.draw();square1.reportArea();square1.reportPerimeter();})});本文作者:flydean程序那些事本文链接:http://www.flydean.com/js-modules/本文来源:flydean的博客欢迎关注我的公众号:《程序与事》最通俗的解读,最深刻的干货,最简洁的教程,还有很多你不知道的小技巧等你来发现!
