初遇sea.js
最近在进行项目的重构,要求用到sea.js进行模块化管理,在项目中接触到sea.js,刚开始用的时候还是有很多地方力不从心,但是用着用着,代码的规范性和模块化的特性越发凸显,使我们摆脱了js编程过程当中的依赖代码关系混乱的现象,提高了JavaScript代码的清晰度和可读性,方便了代码的编写和维护。
sea.js优势
1、使HTML页面更加简洁。只需引入模块的入口函数
2、使文件与文件之间的依赖关系更加清晰。不用像之前考虑每个文件的引入顺序。
3、模块化开发,代码清晰,方便维护。CMD规范(common module definetion)
sea.js是一个遵循CommonJS的JavaScript模块加载框架。顾名思义,使用sea.js 的项目中的文件均需要遵循cmd规范。然而,我们常用到的jquery及其插件一般都不是cmd规范而是amd(具体关于两者的区别不做过多阐述),所以需要我们手动封装成为cmd规范文件。模块化思想及开发原则
在sea.js中,遵循的基本原则是一切皆模块,引入SeaJS后,在编写JavaScript代码的时候其实就是在定义一个模块,在这个模块中可以定义该模块的私有变量也可以定义公用的属性和方法,通过exports/return将其方法暴露出去,供其他需要的模块调用。模块与模块之间可以相互被引用。
另外,一个模块对应一个js文件,也就是一个js中只能出现一个define,即一个模块。
- sea.js的几个关键字用法
seajs.use—-通过 use 方法,可以在页面中加载任意模块。主要用于载入入口模块,一般也只用于载入入口模块,也是程序的一个入口。
//单一模式
<script src="./sea.js"></script>
<script>
seajs.use('./init');//一般不需要加后缀.js
</script>
//单一模式
//如果只有一个入口模块,还可以通过标签直接引入,代码更加简洁
<script src="./sea.js" data-main="./init"></script>
//回调模式
seajs.use('./init', function(a) {
alert('hello seaJS');
});
//多模块模式
seajs.use(['./a', './b'], function(a, b) {
a.run();
b.run();
});`
- seajs.config ——— 可以使用 config 方法来配置加载器.这里简单的提供几个常用的配置。
seajs.config({
base: 'path/to/jslib/',//基址寻址,以后所有require引入的模块均以该地址为相对路径,即引入的文件位于该地址下
alias: { //对较长的地址链接进行缩写
'app': 'path/to/app/'
},
charset: 'utf-8', //下载js时script标签的charset属性
timeout: 20000, //下载文件的最大时长,以毫秒为单位
debug: false //表示是否工作在调试模式下
});
- define ——-定义模块。经查阅,define可以接受3个参数,对于这3个参数的用法,不做过多介绍,我只介绍最常用的方法。
- require——-函数用来访问其他模块提供的 API
- exports ——用来向外提供模块的API(可用return代替)
- module ——–参数存储模块的元信息
- module.exports—-向外暴露模块
define(function(require, exports, module) { //模块定义的一般写法
//模块代码
var $=require('./jquery-1.8.3.min');//经封装的jquery
exports.publicFun=function () { //公共方法,可通过exports将方法暴露出去
alert('I am a public function and every modules can use me when they require me');
};
module.exports = { a: "hello" ,b:"seaJS"};//向外暴露一个对象
function personalFun() {
alert('I am a personal function and only my father can use me');
}
personalFun();//私有方法,外部其他模块无法调用
});
项目中遇到的问题
整个项目做下来,感觉对于sea.js的理解有了一个很大的提升。其中遇到的主要问题是对于jquery以及项目中遇到的jquery以及其他类的插件的cmd封装。需要注意的是,没有封装的js文件在模块化的js中不可使用,已经封装好的模块,除了其暴露出去的属性和方法,一切都相当于一个封闭空间,其他模块都不可使用。要遵循一切皆模块的思想。下面介绍几种的封装的方法。- jquery的封装
//一般的封装
define(function(){
jquery code..
return $.noConflict(true);
});
//兼容非模块化引用的jquery封装方案 :
(function(factory) {
if(typeof define === 'function'){
define(factory);
}else{
factory();
}
})(function(require){
jquery code..
if(require) return $.noConflict(true);
});
//jquery插件封装(回到顶部插件)
define(function (require, exports, moudles) {
return function (jquery) {
(function ($) {
$.fn.scrolltop = function (options) {
var opts = $.extend({}, $.fn.scrolltop.defaults, options);
var run2top = function () {
var scrollDist = $(window).scrollTop();
if (scrollDist > 0) {
scrollDist -= opts.topSpeed;
scrollDist = Math.max(scrollDist, 0);
$(window).scrollTop(scrollDist);
setTimeout(run2top, 1);
}
};
return this.each(function () {
var $this = $(this);
$this.click(function () {
run2top();
});
});
};
$.fn.scrolltop.defaults = {
heightThreshhold: 1000,
width: "50px",
height: "50px",
right: "8%",
bottom: "30px",
topSpeed: 30
};
})(jQuery);
}
});
//弹框插件webox的封装
define(function (require, exports, module) {
var webox = function(option){
var _x,_y,m,allscreen=false;
if(!option){
alert('options can\'t be empty');
return;
};
if(!option['html']&&!option['iframe']){
alert('html attribute and iframe attribute can\'t be both empty');
return;
};
option['parent']='webox';
option['locked']='locked';
$(document).ready(function(e){
$('.webox').remove();
$('.background').remove();
var width=option['width']?option['width']:400;
var height=option['height']?option['height']:240;
$('body').append('<div class="background" style="display:none;"></div><div class="webox" style="width:'+width+'px;height:'+height+'px;display:none;"><div id="inside" style="height:'+height+'px;"><h1 id="locked" onselectstart="return false;">'+(option['title']?option['title']:'')+'<a class="span" href="javascript:void(0);"></a></h1>'+(option['iframe']?'<iframe class="w_iframe" src="'+option['iframe']+'" frameborder="0" width="100%" scrolling="yes" style="border:none;overflow-x:hidden;height:'+parseInt(height-30)+'px;"></iframe>':option['html']?option['html']:'')+'</div></div>');
if(navigator.userAgent.indexOf('MSIE 7')>0||navigator.userAgent.indexOf('MSIE 8')>0){
$('.webox').css({'filter':'progid:DXImageTransform.Microsoft.gradient(startColorstr=#55000000,endColorstr=#55000000)'});
}if(option['bgvisibel']){
$('.background').fadeTo('slow',0.3);
};
$('.webox').css({display:'block'});
$('#'+option['locked']+' .span').click(function(){
$('.webox').css({display:'none'});
$('.background').css({display:'none'});
});
$('.hideBox').click(function(){//取消对话框
$('.webox').css({display:'none'});
$('.background').css({display:'none'});
});
$('.okBox').click(function(){//需要传递参数,更新数据库
var haha = $("input[type=radio][name=flag]:checked").val();
if(haha == null){
show("什么也没有选中");
return;
}
var hehe = $(this).attr("id");
var parmetre = $(this).attr("id")+"="+haha+"&id="+$("#rateSettingId").val();
sendAjax("updateRateSetting",parmetre,function(data){
show(data.message);
if(data.status){
$('.webox').css({display:'none'});
$('.background').css({display:'none'});
$("."+hehe).attr("src","img/op_memo_"+haha+".png");
}
});
});
var marginLeft=parseInt(width/2);
var marginTop=parseInt(height/2);
var winWidth=parseInt($(window).width()/2);
var winHeight=parseInt($(window).height()/2.2);
var left=winWidth-marginLeft;
var top=winHeight-marginTop;
$('.webox').css({left:left,top:top});
$('#'+option['locked']).mousedown(function(e){
if(e.which){
m=true;
_x=e.pageX-parseInt($('.webox').css('left'));
_y=e.pageY-parseInt($('.webox').css('top'));
}
}).dblclick(function(){
if(allscreen){
$('.webox').css({height:height,width:width});
$('#inside').height(height);
$('.w_iframe').height(height-30);
$('.webox').css({left:left,top:top});
allscreen = false;
}else{
allscreen=true;
var screenHeight = $(window).height();
var screenWidth = $(window).width();$
('.webox').css({'width':screenWidth-18,'height':screenHeight-18,'top':'0px','left':'0px'});
$('#inside').height(screenHeight-20);
$('.w_iframe').height(screenHeight-50);
}
});
}).mousemove(function(e){
if(m && !allscreen){
var x=e.pageX-_x;
var y=e.pageY-_y;
$('.webox').css({left:x});
$('.webox').css({top:y});
}
}).mouseup(function(){
m=false;
});
$(window).resize(function(){
if(allscreen){
var screenHeight = $(window).height();
var screenWidth = $(window).width();
$('.webox').css({'width':screenWidth-18,'height':screenHeight-18,'top':'0px','left':'0px'});
$('#inside').height(screenHeight-20);
$('.w_iframe').height(screenHeight-50);
}
});
};
module.exports = webox;
});
注意:以上两个插件的封装其实是一种,都是需要把方法和属性暴露出去,暴露的方法可以用exports/return两种方式实现。因为jquery插件的封装有很多方式,自我感觉,最大的难点是因为看不懂插件而不知如何封装,这个就需要我们多下功夫弄懂插件了。