当前位置: 首页 > 后端技术 > Node.js

JavaScript模块化编程探索

时间:2023-04-04 00:23:34 Node.js

随着网站逐渐成为“互联网应用”,网页中嵌入的Javascript代码变得越来越大、越来越复杂。网页越来越像桌面程序,需要一个团队一起工作,进度管理,单元测试等等……开发者不得不用软件工程的方法来管理网页的业务逻辑。Javascript模块化编程已经成为一种迫切的需求。从CommonJS来讲,CommonJS团队定义了模块格式来解决JavaScript作用域问题,保证每个模块都在自己的命名空间下执行。根据CommonJS规范,每个文件都是一个具有自己范围的模块。文件中定义的变量、函数和类都是私有的,对其他文件不可见。CommonJS规范规定,在每个模块内部,模块变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是一个外部接口。加载模块实际上是加载模块的module.exports属性。CommonJS提供了两个工具来实现模块之间的依赖关系:require()用于导入当前作用域中已有的模块模块对象,用于从当前作用域中导出一些东西,那么我们先来个Helloworld的小栗子来尝试一下!写一个简单的JavaScript模块,新建一个项目文件夹,虽然项目很小。..命名为commonjs,在里面新建两个JavaScript文件,分别命名为world.js和salute.js,代码如下://salute.jsgreetingvarMySalute="Hello";module.exports=MySalute;/*Note上面和下面分别写在2个文件中js文件*///world.jsvarMySalute=require("./salute");varResult=MySalute+"world!";console.log(Result);然后我新懵懂的新建了一个demo.html,(想用浏览器打开看看长什么样子)内容如下:文档结果在浏览器中打开,看控制台,大失所望,报错world.js:2UncaughtReferenceError:requireisnotdefined发现浏览器不兼容CommonJS的根本原因是缺少四个Node.js环境变量:moduleexportsrequireglobal只要能提供这四个变量,浏览器就可以加载CommonJS模块,问题可以解决,不过好像不是很好玩,有兴趣的朋友可以去阮老师的博客访问传送门现在我决定去Node.js玩Node在.js环境下玩转,打开命令行工具cd到项目目录:结果成功打印出Helloworld!果然很有意思,呵呵。至此,简单的实现了模块之间的引用。在这个CommonJS规范下还能做什么?去CommonJS官网查到如下内容:JavaScript是一门强调大的面向对象语言,并且拥有最快的解释器,和之前的JavaScriptt定义的API仅用于构建浏览器端应用程序。但是,使用这个CommonJS,可以构建更广泛的应用程序。具体点是它们可以用JavaScript编写:服务器端应用程序服务器端JavaScript应用程序命令行工具命令行工具基于GUI的桌面应用程序基于桌面GUI的应用程序混合应用程序(Titanium、AdobeAIR)(这是什么?虽然现在还不知道,但是感觉很牛逼)AMD是用来干什么的?AMD(AsynchronousModuleDefinition)对异步模块定义的理解很肤浅。.起初,人们可能会认为CommonJS的本质是同步,它的模块系统不适合浏览器,而这次AMD指定了一个标准来向其他人证明模块化的JavaScript可以异步加载依赖项并解决同步加载的问题。.定义define函数是AMD定义模块的方法:define(id?:String,dependencies?:String[],factory:Function|Object);id:指定模块的名称dependencies:指定依赖工厂:它定义了模块,可以是函数或者对象,如果是函数那么函数的返回值就是模块导出的值。examplesdefine('myModule',['jquery'],function($){//$是jquery模块的导出。$('body').text('helloworld');});//并使用itrequire(['myModule'],函数(myModule){});RequireJS随着网站功能的逐渐丰富,网页中的js也变得越来越复杂和臃肿。本来是用script标签一个一个导入js文件的,这种方式已经不能适应现在的互联网开发模式了。我们需要团队协作、模块复用、单元测试等一系列复杂的需求。RequireJS是一个非常紧凑的JavaScript模块加载框架,是AMD规范的最佳实现者之一。最新版本的RequireJS压缩后只有14K,非常轻量级。它还可以同时与其他框架一起工作。使用RequireJS肯定会提高你的前端代码的质量。这段出处对比了js模块的同步加载和异步加载。CommonJS规范加载模块是同步的,也就是说只有加载完成后,才能进行后续操作。AMD规范是一个异步加载的模块,允许指定回调函数。由于Node.js主要用于服务端编程,模块文件一般已经存在于本地硬盘上,所以加载速度更快,而且不需要考虑异步加载的方式,所以CommonJS规范更适用。但是如果是浏览器环境,则必须从服务器端加载模块,此时必须采用异步方式,所以浏览器端一般采用AMD规范。浏览器同步加载js模块创建a.js(function(){functiontest(){alert("itworks");}test();})()新建demo1.html文档body在浏览器中运行demo1.html,执行alert时,html内容为空白,即body并没有显示出来,只有点击确定后才会出现。这是JS阻塞浏览器渲染的结果。RequireJS异步加载js模块,重写a.js如下:define(function(){functiontest(){alert("itworks");}test();})去github下载require.jsRequireJS下载和修改demo1.html如下:!DOCTYPEhtml>Documentbody浏览器提示“itworks”,说明操作正确,但有一处不同。这时候浏览器不空白了,body已经出现在页面上了。至此,我们可以知道requirejs有以下优点:防止js加载阻塞页面渲染管理模块之间的依赖,方便代码编写和维护,让代码更加优雅。CMD规范CMD(CommonModuleDefinition)模块定义规范。本规范明确了模块的基本书写格式和基本交互规则。在CMD规范中,一个模块就是一个文件。代码的书写格式如下:define(factory);define是一个全局函数,用于定义模块。define接受工厂参数,可以是函数、对象或字符串。当工厂是对象或字符串时,表示模块的接口是对象或字符串。例如,您可以按如下方式定义一个JSON数据模块:define({"foo":"bar"});require是一种接受模块ID作为唯一参数获取其他模块提供的接口的方法。define(function(require,exports){//获取模块a的接口vara=require('./a');//调用模块a的方法a.doSomething();});require.async方法用于在模块内部异步加载模块,加载完成后执行指定的回调。回调参数是可选的。define(function(require,exports,module){//异步加载一个模块,加载完成后执行回调require.async('./b',function(b){b.doSomething();});//异步加载多个模块,加载完成后执行回调require.async(['./c','./d'],function(c,d){c.doSomething();d.做一点事();});});require是同步执行的,require.async是异步执行的。require.async一般用于加载可以异步延迟的模块。更多细节请参考CMD模块定义规范sea.jsRequireJS和Sea.js都是模块加载器,提倡模块化开发的理念,其核心价值是让JavaScript模块化开发变得简单自然。在SeaJS中,所有的JavaScript文件都应该以模块的形式编写,一个文件只包含一个模块。使用全局函数define来定义一个模块:define(id?,dependencies?,factory);id是当前模块的唯一标识符。此参数是可选的。如果不指定,默认为模块所在文件的访问路径。如果指定,则必须是顶级ID或绝对ID(而非相对ID)。dependencies当前模块所依赖的模块是一个模块标识符数组。此参数是可选的。如果未指定,模块加载器将解析来自factory.toString()的数组。工厂模块的工厂功能。当模块初始化时,这个工厂函数只被调用一次。factory可以是函数,也可以是任何值,例如对象或字符串。在这种情况下,module.exports将直接设置为factory的值。调用工厂函数时,总会传入三个参数:require、exports和module,这三个参数在所有模块代码中都有。define(function(require,exports,module){//模块代码在这里});more