前言 |
前端开发中,起初只要在script
标签中嵌入几十上百行代码就能实现一些基本的交互效果,后来js得到重视,应用也广泛起来了,jQuery,Ajax,Node.Js,MVC,MVVM
等的助力也使得前端开发得到重视,也使得前端项目越来越复杂,然而,JavaScript
却没有为组织代码提供任何明显帮助,甚至没有类的概念,更不用说模块(module)了,所以,进行模块化开发的重要性就不言而喻了。那么什么是模块呢?
一个模块就是实现特定功能的文件,有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块。
所以,今天小编介绍一下js模块化开发的几种方法。
一、模块化开发规范 |
js模块化的开发并不是随心所欲的,为了便于他人的使用和交流,需要遵循一定的规范。目前,通行的js模块规范主要有两种:CommonJS
和AMD
。
1.1AMD
AMD 即Asynchronous Module Definition
,中文名是“异步模块定义”的意思。它是一个在浏览器端模块化开发的规范,服务器端的规范是CommonJS;
模块将被异步加载,模块加载不影响后面语句的运行。所有依赖某些模块的语句均放置在回调函数中。
关于AMD有两个非常重要的概念,那就是用于模块定义的define方法和用于处理依赖加载的require方法。
(1)作为一个规范,只需定义其语法API,而不关心其实现。define函数定义如下:
1 define(2 ?????[module-name?] /*可选*/, 3 ?????[array-of-dependencies?] /*可选*/, 4 ?????[module-factory-or-object]5 ?);
- module-name: 模块标识,可以省略。如果没有这个属性,则称为匿名模块。
- array-of-dependencies: 所依赖的模块,可以省略。
- module-factory-or-object: 模块的实现,或者一个JavaScript对象
举个栗子:
define( ???"myModule", ???["foo", "bar"], ???// 模块定义函数,依赖(foo,bar)作为参数映射到函数上 ???function (foo, bar) { ???????// 创建模块 ???????var myModule = { ???????????myFun: function () { ???????????????console.log("Jeri"); ???????????} ???????} ???????// 返回定义的模块 ???????return myModule; ???});
(2)require()方法,有两个参数:
require([module], callback);
其中:
- 第一个参数[module],是一个数组,里面的成员就是要加载的模块;
- 第二个参数callback,则是加载成功之后的回调函数。
举个实际栗子:
1 require([‘math‘], function (math) {2 math.add(2, 3);3 });
1.2CommonJS规范
CommonJS
是服务器端模块的规范,根据CommonJS
规范,一个单独的文件就是一个模块。每一个模块都是一个单独的作用域,也就是说,在该模块内部定义的变量,无法被其他模块读取,除非定义为global
对象的属性。输出模块变量的最好方法是使用module.exports
对象。
不过,与AMD表现形式不同的是,CommonJS模块并不使用define进行定义。CommonJS模块由两部分组成:变量exports和require函数。
(1)exports/require:加载模块使用require
方法,该方法读取一个文件并执行,最后返回文件内部的module.exports
对象。
再举个栗子:创建两个js文件:
libJS:
1 // 新定义的模块方法2 function log(arg) {3 ????console.log(arg);4 }5 6 // 把方法暴露给其他模块7 exports.log = log;
app.JS:
1 // ./lib是我们需要的一个依赖 2 var lib = requrie("./lib"); 3 ?4 // 新定义的模块方法 5 function foo() { 6 ????lib.log("jeri"); 7 } 8 ?9 // 把方法暴露给其他模块10 exports.foo = foo;
(2)module.exports
直接举栗子:
1 var i = 1;2 var max = 30;3 4 module.exports = function () {5 ??for (i -= 1; i++ < max; ) {6 ????console.log(i);7 ??}8 ??max *= 1.1;9 };
二、JS模块的常用写法 |
在前言中小编也提过,模块:就是一个模块就是实现特定功能的文件,有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块。
除了接下来要讲的require.js,还有适合于一些简单模块的几种常用写法。
2.1字面量写法
要知道,模块就是实现特定功能的一组方法。
只要把不同的函数(以及记录状态的变量)简单地放在一起,就算是一个模块。这也是最简单的一种写法。也就是说,每一个函数就是一个简单的模块。
1 function func1 (){2 ??alert("这是func1");3 };4 function func2 (){5 ??alert("这是func2");6 }
把栗子中的两个函数func1()和func2()就组成了一个模块;
不过,这种写法有着一定的缺点:
如果只是实现一些简单的功能还好,如果实现复杂的功能,太多的函数就会出现问题,"污染"了全局变量,无法保证不与其他模块发生变量名冲突,而且模块成员之间看不出直接关系。
2.2对象写法
所以,为了解决上述的问题,把模块写成一个对象,所有的模块成员都放到这个对象里面不失为一个好的方式。
再再举个栗子:
1 var module = { 2 ????func1 : function(){ 3 ??????alert(1); ??????4 ????} 5 ?6 ????func2 : function(){ 7 ??????alert(2); ??????8 ????} 9 }10 11 module.func1();12 module.func2();
2.3通过闭包实现
在JavaScript中,并不能可以直接声明类,但我们可以使用闭包来封装私有的属性和方法,进而模拟类的概念,在JavaScript中实现Module模式;
疯狂的举栗子:
1 var myModule = (function () { 2 ?3 ????// 私有变量 4 ????var privateVar = 0; 5 ?6 ????// 私有函数 7 ????var privateFun = function (foo) { 8 ????????console.log(foo); 9 ????};10 11 ????return {12 ????????// 私有变量13 ????????publicVar: "foo",14 15 ????????// 公有函数16 ????????publicFun: function (arg) {17 18 ????????????// 修改私有变量19 ????????????privateVar ++;20 21 ????????????// 传入bar调用私有方法22 ????????????privateFun(arg);23 ????????}24 ????};25 }) ()
2.4输入全局变量
独立性是模块的重要特点,模块内部最好不与程序的其他部分直接交互。为了在模块内部调用全局变量,必须显式地将其他变量输入模块。
1 var module1 = (function ($, YAHOO) {2 //...3 })(jQuery, YAHOO);
三、require.js |
requir.JS本就是为了实现js的模块开发而创建的一个js脚本,其主要有着两大优点:
(1)实现js文件的异步加载,避免网页失去响应;
(2)管理模块之间的依赖性,便于代码的编写和维护。
3.1require.js的实现步骤
(1)众所周知,要导入js文件,为了避免网页失去响应有两种方法;
第一种,把<script></script>放在文件的最后。
今天,我们主要说第二种:
<script src="js/require.js" defer async="true" ></script>
async属性表明这个文件需要异步加载,避免网页失去响应。但是,IE不支持这个属性,只支持defer,所以需要把defer也写上。
(2)在HTML中导入require.JS文件后,就可以编写自己的main.js文件。
首先,导入main.js文件:
<script src="js/require.js" data-main="js/main"></script>
其中:data-main属性的作用是,指定网页程序的主模块。
在上例中,就是js目录下面的main.js,这个文件会第一个被require.js加载。由于require.js默认的文件后缀名是js,所以可以把main.js简写成main。
(3)编写main.js;
1 require([‘moduleA‘, ‘moduleB‘, ‘moduleC‘], function (moduleA, moduleB, moduleC){2 // some code here3 });
通过require方法,实现代码的模块加载,传入两个参数:
①第一个参数是一个数组,表示代码所依赖的模块。
②第二个参数是一个回调函数,当前面的模块加载完成后,就会被调用。
加载的模块一参数的形式传入,从而在回调函数内部可以使用这些模块,回调函数就是整个页面的JS代码;
再次疯狂的举栗子:
1 require([‘jquery‘, ‘underscore‘, ‘backbone‘], function ($, _, Backbone){2 // some code here3 });
3.2模块的加载
在上面的栗子中,我们可以使用require.config()方法,对模块的加载行为进行自定义。
(1)require.config()就写在主模块(main.js)的头部。参数就是一个对象,这个对象的paths属性指定各个模块的加载路径。
不要命的举栗子:
1 require.config({2 paths: {3 "jquery": "jquery.min",4 "underscore": "underscore.min",5 "backbone": "backbone.min"6 }7 });
(2)上面的代码给出了三个模块的文件名,路径默认与main.js在同一个目录(js子目录)。
如果这些模块在其他目录,比如js/lib目录,则有两种写法。
一种是逐一指定路径。
再次举栗子:
1 require.config({2 paths: {3 "jquery": "lib/jquery.min",4 "underscore": "lib/underscore.min",5 "backbone": "lib/backbone.min"6 }7 });
另一种则是直接改变基目录(baseUrl)。
再再次举栗子:
require.config({ baseUrl: "js/lib", paths: { "jquery": "jquery.min", "underscore": "underscore.min", "backbone": "backbone.min" } });
(3)如果某个模块在另一台主机上,也可以直接指定它的网址;
举栗子:
require.config({ paths: { "jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min" } });
3.3require的AMD模块写法
require.js加载的模块,采用AMD规范。也就是说,模块必须按照AMD的规定来写。
具体来说,就是模块必须采用特定的define()函数来定义。如果一个模块不依赖其他模块,那么可以直接定义在define()函数之中。
假定现在有一个math.js文件,它定义了一个math模块。那么,math.js就要这样写:
1 // math.js 2 ?3 define(function (){ 4 ?5 var add = function (x,y){ 6 ?7 return x+y; 8 ?9 };10 11 return {12 13 add: add14 };15 16 });
加载方法如下:
1 // main.js2 3 require([‘math‘], function (math){4 5 alert(math.add(1,1));6 7 });
如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,指明该模块的依赖性。
1 define([‘myLib‘], function(myLib){ 2 ?3 function foo(){ 4 ?5 myLib.doSomething(); 6 ?7 } 8 ?9 return {10 11 foo : foo12 13 };14 15 });
作者编 |
在日常的工作中,js的模块法开发是一个不可或缺的一部分。在这里只是介绍了基于AMD模块的require.js。还有基于commonJS规范的node.js。小编会在下次和大家一起学习。
JS模块化开发----require.js
原文地址:http://www.cnblogs.com/zxt-17862802783/p/7822226.html