配色: 字号:
使用require.js编写模块化JS
2016-10-24 | 阅:  转:  |  分享 
  
使用require.js编写模块化JS

layout:post

title:使用requirejs编写模块化代码

category:javascript

date:2016-10-2200:00:00

tags:



javascript

requirejs

写在前面



最早接触javascript的时候,javascript代码直接扔在script标签里面就完事了。



反正代码不多,交互简单,逻辑不难,和HTML混在一起也未尝不可。



后来交互越来越复杂,代码越多越多了,我们就开始把JS代码独立到了单独的JS文件中。



公共的库引用在前,自己的逻辑代码引用在后,全局变量定义在HTML内部,在独立JS文件中直接使用变量就好。



我们会经常看到下面这种代码:















通过script标签顺序去js管理依赖关系。



阮一峰老师在Javascript模块化编程(三):require.js的用法

一文中总结了这样写法的缺点:



首先,加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长;



其次,由于js文件之间存在依赖关系,因此必须严格保证加载顺序(比如上例的1.js要在2.js的前面),依赖性最大的模块一定要放到最后加载.



当依赖关系很复杂的时候,代码的编写和维护都会变得困难。



而requirejs的诞生便是为了解决这个问题。



requirejs



在官网把requirejs下载回来之后。使用一般的方法引入:





但是这样的方法,还是可能在加载require.js的时候导致网页失去响应。解决方案一般有两种:



把上面的代码放到网页底部



使用异步的方法加载,如下:





async属性表明这个文件需要异步加载,避免网页失去响应。



不过IE下不支持这个属性,只支持defer,所以可以把defer也写上。



加载主模块



在上一步,我们已经引入了require了,那么require怎么知道我们究竟要加载什么东西呢?答案是使用data-main属性。

假设我们的主模块为js/home.js,引入代码应该如下:





//require.js默认文件后缀为js,所以home.js可以写成home。

接下来我使用58HouseSearch的代码来讲解一下require的使用。



在此项目里面,重构前大概就是JS变量漫天飞,js文件里面各种函数到处乱放。一开始用起来还没什么,后来加入了更多功能的时候,JS代码维护起来就疼不欲生了。因此托了个小伙伴帮忙使用模块化思想重构了一下JS代码。



上面说了,我们首先需要创建我们的模块,在这个项目里面,主模块叫home.js。



home.js中我们需要配置一下require.config.



require.config({

baseUrl:''/DomainJS/'',

paths:{

jquery:"lib/jquery-1.11.3.min",

"AMUI":"lib/amazeui.2.7.1.min",

"jquery.range":"lib/jquery.range",

"es5":"lib/es5",

"mapController":"mapController",

"addToolbar":"addToolbar",

},

shim:{

"addToolbar":{

deps:["jquery"]

},

"jquery.range":{

deps:["jquery"]

}

}

});

在这里我主要配置了一下baseURL(所有模块的查找根路径),paths(名称映射),shim(

为那些没有使用define()来声明依赖关系、设置模块的"浏览器全局变量注入"型脚本做依赖和导出配置。)



关于require.config的详细内容可以看下下面这些文章:



RequireJS进阶:配置文件的学习

RequireJS进阶:模块的优化及配置的详解

配置做完了,我们也可以开始真正写我们的逻辑代码了,我们使用require来加载我们需要的库。

代码如下:



require([''domready!'',''jquery'',''AMUI'',''mapController'',''city'',''commuteGo''],



function(doc,$,AMUI,mapController,city,commuteGo){

city.initAllCityInfo();

mapController.init();



$("input[name=''locationType'']").bind(''click'',

mapController.locationMethodOnChange)



$("input[name=''vehicle'']").bind(''click'',commuteGo.go)



$(''#Get58Data'').bind(''click'',function(e){

e.preventDefault();



mapController.Get58DataClick();

e.stopPropagation();

});





$.ajax({

type:"post",

url:"../Commom/GetPVCount",

data:{},

success:function(result)

{

if(result.IsSuccess){

$("#lblPVCount").text(result.PVCount);

}else{

$("#lblPVCount").text(0);

console.log(result.Error);

}

}

});



$(''#search-offcanvas'').offCanvas({effect:''overlay''});



$(".amap-sug-result").css("z-index",9999);

})

忽略function里面的具体逻辑,加载如下:



require([''domready!'',''jquery'',''AMUI'',''mapController'',''city'',''commuteGo''],

function(doc,$,AMUI,mapController,city,commuteGo){



//todo



});

第一个参数为一个数组,表示所依赖的模块,此处为[''domready!'',''jquery'',''AMUI'',''mapController'',''city'',''commuteGo''];



第二个参数为回调函数,当前面指定的模块都全部加载成功之后,便调用此函数。加载的模块会以参数形式传入此函数,从而在回调函数内部就可以使用这些模块啦。



require()异步加载所需模块的时候,此时浏览器并不会失去响应;当前面的模块加载成功之后,执行回调函数才会运行我们的逻辑代码,因此解决了依赖性问题。



讲完了模块加载,我们下面讲一下模块编写。



AMD模块编写



require.js加载的模块的采用的AMD规范。所以我们的模块必须按照AMD的规定来写。



关于AMD规范详情可以看这个文章:Javascript模块化编程(二):AMD规范



模块有两个情况,不依赖其他模块和依赖其他模块。



不依赖其他模块



直接define定义,使用function回调。



58HouseSearch/DomainJS/helper.js



define(function(){



//获取URL中的参数

vargetQueryString=function(name){

varreg=newRegExp("(^|&)"+name+"=([^&])(&|$)");

varr=window.locationwww.wang027.com.search.substr(1).match(reg);

if(r!=null)returnunescape(r[2]);returnnull;

}

return{

getQueryString:getQueryString,

};

})

依赖其他模块



define中如同require一样,用数组表明需要加载的模块,function回调。



58HouseSearch/DomainJS/marker.js



define([''mapSignleton'',''city'',''transfer''],

function(mapSignleton,city,transfer){

var_map=mapSignleton.map;

var_workMarker=null;

var_markerArray=[];

varload=function(x,y,locationName){

_workMarker=newAMap.Marker({

map:_map,

title:locationName,

icon:''http://webapi.amap.com/theme/v1.3/markers/n/mark_r.png'',

position:[x,y]

});

}



varadd=function(address,rent,href,markBG){

newAMap.Geocoder({

city:city.name,

radius:1000

}).getLocation(address,function(status,result){



if(status==="complete"&&result.info===''OK''){

vargeocode=result.geocodes[0];

varrentMarker=newAMap.Marker({

map:_map,

title:address,

icon:markBG?''IMG/Little/''+

markBG:''http://webapi.amap.com/theme/v1.3/markers/n/mark_b.png'',

position:[geocode.location.getLng(),geocode.location.getLat()]

});

_markerArray.push(rentMarker);



rentMarker.content="

+href+"''>房源:"+address+"租金:"+rent+"
"

rentMarker.on(''click'',function(e){

transfer.add(e,address);

});

}

})

};



varclearArray=function(){

if(_markerArray&&_markerArray.length>0)

_map.remove(_markerArray);

_markerArray=[];

}



varclear=function(){

if(_workMarker){

_map.remove(_workMarker);

}

}



return{

load:load,

add:add,

clearArray:clearArray,

clear:clear

};

});

这样的话,一个供require调用的模块也就写好了。

献花(0)
+1
(本文系thedust79首藏)