js的模块化探究

这篇文章观点主要来源阮一峰的博客及自己的理解,这里写出来一是为了加强自己的记忆,二是希望和大家一起分享,如果有错误希望大家指出。
模块通俗来说就是包含有很多方法的集合,感觉有点像别的语言中的库文件,在网页应用中JavaScript的使用越来越广泛,一个网页中所用到的js文件也越来越多,管理起这些文件就显得非常混乱,首先看看js的模块化写法,

js的模块化

如果将所有方法都直接写在全局域里面,那么就会污染全局变量,甚至占用很多有用的名字,取名也会费一番脑经,这种方式很不适合大型开发

function f1(){
//...
}
function f2(){
//...
}
//...

改进的写法就是将这些方法写入一个对象中,但是如果模块里面有一些不希望暴露给使用者的属性或方法,就没有办法了

var module=new Obeject(
f:1,//不希望给外面调用
f1:function(){
//...
},
f2:function(){
//...
},
//...
);

这就出现了立即执行函数这种写法,这个函数返回一个希望暴露给外部的一系列方法或属性的对象集合(甚至还可以用新的名字去替换原先模块里的名字),并直接定义一个变量去引用这个对象

var module1=(function(){
f:1,//不希望给外面调用
var f1=function(){
//...
}
var f1=function(){
//...
},
//...
return {
out_f1:f1,
out_f2:f2
};
})();

这里将f这个属性向外界隐藏了,这种方式还可以对function传入其他全局变量甚至传入它自己,用以分次添加模块的方法,可以很方便的临时添加功能

//传入其他全局变量,比如类库
var module2=(fuction($,mod,mod1){
//使用jquery操作等
mod.f3=mod1.dosomething;//将别的模块的方法添加到这里来
return mod;//返回修改后的module2
})(Jquery,module2,module3)

现行的模块规范有两种,一种是CommonJs,另一个是AMD。
由于nodejs的出现,让JavaScript不再局限于网页端编程,进入了服务器的领域,对于复杂的后端逻辑避免不了大量的模块引用,在nodejs中,模块是采用CommonJs的规范写的,使用下面的方式去加载和使用一个写好的math模块

var math = require('math');
math.add(2,3); // 5

这种方式在浏览器环境中是不适合的,要使用这个模块的方法必须等这个加载过程结束才可以,模块存储在服务器端,从浏览器端加载模块会因为各种各样的原因导致加载缓慢,这样页面就会停滞,这会大大影响用户的体验。
因此在浏览器端不适合同步加载,必须使用异步加载,于是AMD规范出现了,AMD是"Asynchronous Module Definition"的缩写,意思就是“异步模块定义”。 它采用的也是require方法去加载模块,但是它的参数带有一个回调函数,也就是加载成功就会执行那个函数

require([module], callback);//两个参数,一个模块名,一个回调函数
require(['math'], function (math) {
math.add(2, 3);
});

现在主要有两个Javascript库实现了AMD规范:require.jscurl.js

require.js的使用

最早的时候,js文件只需要几个就够了,js文件之间的依赖关系也没有那么密切,然而现在的情况不同了,经常会见到下面的代码

<script src="1.js"></script> 
<script src="2.js"></script>
<script src="3.js"></script>
<script src="4.js"></script>
<script src="5.js"></script>
<script src="6.js"></script>

相互之间还会有依赖关系,比如很多js文件都很依赖jquery这个库,所以如果加载顺序不同,会很麻烦,因此要严格遵照依赖性顺序,依赖性最大的放到最后,require.js的出现主要是为了两个问题,一是实现异步加载js文件,防止网页失去响应,二是为了管理模块之间的依赖关系,方便编写和维护。
和别的js模块一样,要使用它首先要去官网下载一个最新的版本,加载这个文件时可能也会让网页失去反应,可以在网页的最后面去加载,也可以使用下面的方式加载

<script src="js/require.js" defer async="true" ></script>   

其中defer是为了支持IE,它不支持async属性,这样就可以异步加载这个js文件了,这样只是加载了require.js,还要给data-main属性写一个入口文件,制定网页程序的主模块

<script src="js/require.js" data-main="js/main"></script>

其中main.js就是我们的主模块,假设也是在js文件下,

主模块的写法

如果不依赖任何模块,就可以直接写JavaScript代码,但是这就没必要用require.js了,
要加载模块,就要使用require函数

// main.js
require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){
// some code here
});

一般的加载方式就是这样,首先接受一个模块数组参数,然后有一个回调函数,数组作为函数的参数传入,模块数组中的模块都是异步加载的

模块的加载

在下面的例子中

require(['jquery', 'underscore', 'backbone'], function ($, _, Backbone){
// some code here
});

加载了三个模块,但是并没有提这些模块的具体地址、也没有对回调函数的传参使用别名,这里还需要在require.config方法中进行设置

require.config({
baseUrl:"js/lib",//相对main.js的目录位置
path:{
"jquery": "jquery.min",//在这个目录下的文件名,可忽略js后缀
"underscore": "underscore.min",
"backbone": "backbone.min"
}
});

还可以直接写cdn地址

require.config({
paths: {
"jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min"
}
});

AMD模块的写法

require.js要求加载的模块按照AMD的规范去写
假设有一个math模块,就要这样去写

\\mah.js
define( function()
{
var add=function(x,y)
{
return x+y;
};
return{add:add}
});

加载

//main.js
require(["math"],function(math){
alert(math.add(1,2));
});

如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,指明该模块的依赖性。

 define(['myLib'], function(myLib){
   function foo(){
    myLib.doSomething();
    }
    return {
      foo : foo
    };
  });

加载非AMD规范模块

实际上有很多模块并不是按照AMD规范去写的,require.js加载这些模块需要在require.config中的shim属性中添加相应的依赖关系和导出标记

require.config({
shim:{
'underscore':{
exports:"_"
},
'backbone':{
deps:['underscore', 'jquery'],
exports: 'Backbone'
}
}
});

require.js还提供一系列[插件],实现一些特定的功能。
domready插件,可以让回调函数在页面DOM结构加载完成后再运行。

  require(['domready!'], function (doc){
    // called once the DOM is ready
  });

text和image插件,则是允许require.js加载文本和图片文件。

  define([
    'text!review.txt',
    'image!cat.jpg'
    ],    function(review,cat){
      console.log(review);
      document.body.appendChild(cat);
    }
  );

类似的插件还有json和mdown,用于加载json文件和markdown文件。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容