【js模块化+promise】让弹窗们弹起来

弹窗或者浮层是页面上看起来微不足道,却又常常起到重要作用的“零件”。比如最近做的项目中,一个页面上涉及了6个以上的弹窗及其对应的逻辑处理,可以说页面几乎是由弹窗逻辑构成的。因此,如何让这些弹窗优雅地弹起来(别人能够很快地对这些代码进行维护),是一个很重要的问题。

对于弹窗的实现,比较旧的做法是直接在html上加上弹窗的元素,在不需要的时候先将其隐藏,通过js逐个实现弹窗的展示和逻辑。但是这种做法在页面弹窗较多或者多个页面使用相同弹窗时,是非常繁琐的,且代码不易维护,复用度低。因此,本文将介绍js模块化结合promise打造弹窗的方法。

1.       js模块化

本文主要介绍弹窗的实现,因此不再过多赘述什么是js模块化,也不去对比AMD规范和CMD规范,如有疑问的可以参考Javascript模块化编程。后面将用具体的例子,一步一步说明弹窗的实现过程,例子中使用的js模块加载框架为SeaJS。

什么是SeaJS?

SeaJS的主要目的是令JavaScript开发模块化并可以轻松愉悦进行加载,将前端工程师从繁重的JavaScript文件及对象依赖处理中解放出来,可以专注于代码本身的逻辑。SeaJS可以与jQuery这类框架完美集成。使用SeaJS可以提高JavaScript代码的可读性和清晰度,解决目前JavaScript编程中普遍存在的依赖关系混乱和代码纠缠等问题,方便代码的编写和维护。

Ø  定义弹窗模块

SeaJS遵循CMD规范,因此一个模块就是一个js文件,通过define方法来定义,模块定义的方法如下:

define(function(require, exports, module) {
    // 模块代码
});

Ÿ   exports: 是一个对象,用来向外提供模块接口。通过exports.func=function(){};语句,则可以直接将该方法通过exports暴露出去,不需要将其return。当然也可以通过return的方法来向外提供接口。Ÿ   require: 是一个方法,接受模块标识作为唯一参数,用来获取其他模块提供的接口。也即是说在一个模块内部需要加载其他模块,可以通过require(‘./path/another’)实现。

Ÿ   module: 是一个对象,上面存储了与当前模块相关联的一些属性和方法。

本文的示例中,将弹窗定义到文件sample.js中,代码如下所示:

define(function (require, exports, module) {
    //弹窗dom,此处借用了之前项目中的弹窗样式
var tpl = '
Notice
   
'; // 暴露给外部的弹窗加载方法 exports.init = function () { // 加载弹窗 $('body').append(tpl); var pop = $('.sample-pop'); // button点击事件绑定 pop.find('.button').click(function() { pop.remove(); }); }; });

Ø  弹窗模块的调用 

在一个模块进行其他模块的加载,使用require方法,前面已经讲过。在页面上进行模块加载时,使用seajs.use。

// 加载一个模块,在加载完成时,执行回调
seajs.use('./a', function(a) {
    a.doSomething();
});

这里,我们在页面上添加一个button,用于触发弹窗,代码如下所示:



	
	seajs
	
	
	


	


此时,将弹窗模块化的工作算是告一段落,点击页面上的button,则可以加载出弹窗如下图所示:

【js模块化+promise】让弹窗们弹起来_第1张图片

Ø  通用的弹窗模块

为了在多个页面复用这个弹窗(或者一个页面的不同弹窗需求),我们需要让这个弹窗更加通用,比如文案,长宽自定义。因此我们需要对弹窗模块进行一些修改,代码如下所示:

define(function (require, exports, module) {
    var tpl = '…此处省略模板定义,同上…'; 
    exports.init = function (content, style, buttonConfirm, buttonCancel) {
        $('body').append(tpl);
var pop = $('.sample-pop');
pop.find('.survey-confirm-content').text(content);
if (style && style['height']) {
    pop.find('.survey-confirm').css('height',style['height']);
}
if (style && style['width']) {
    pop.find('.survey-confirm').css('width',style['width']);
}
if (buttonConfirm != 'Confirm') {
    if (buttonConfirm === null) {
    pop.find('#survey-confirm-y').hide();
}
else {
        pop.find('#survey-confirm-y').val(buttonConfirm);
}
}
if (buttonCancel != 'Cancel') {
    if (buttonCancel === null) {
    pop.find('#survey-confirm-n').hide();
}
else {
        pop.find('#survey-confirm-n').val(buttonCancel);
}
}
pop.find('.button').click(function() {
    pop.remove();
});
    };
});

而页面上,我们添加三个button来分别触发三个不同的弹窗调用,代码如下所示:



	
	seajs
	
	
	


	
	
	


三个弹窗的效果依次如下图所示:

【js模块化+promise】让弹窗们弹起来_第2张图片

  

好的,到这里我们算是实现了可定制化复用的弹窗模块。接下来,我们该操心另一件事情了,那就是弹窗按钮的点击逻辑。

2.       Promise

什么是Promise?

Promise是CommonJS的规范之一,拥有resolve、reject、done、fail、then等方法,能够帮助我们控制代码的流程,避免函数的多层嵌套。如今异步在web开发中越来越重要,对于开发人员来说,这种非线性执行的编程会让开发者觉得难以掌控,而Promise可以让我们更好地掌控代码的执行流程,jQuery等流行的js库都已经实现了这个对象

在我们的例子中将使用JQuery的promise,来实现对弹窗逻辑的异步处理。JQuery提供了一个方法jQuery.Deferred(),用以创建一个Deferred对象。Deferred对象从名字可以看出,是一个延迟处理的方案。它提供了多个方法,用以实现在未来的某个时间点执行我们期望的回调。下面用具体的例子说明deferred方法的使用。

我们给上一节的三个弹窗分别命名为a、b、c,现在我们需要实现这样的逻辑,弹窗a弹出后,点击确定则弹出弹窗b,点击取消则弹出弹窗c(当然也可以有更复杂的逻辑,我们这里只是为了说明deferred)。

按照旧方法,我们是需要给弹窗a的两个按钮分别绑定点击处理事件的。对于这种弹窗的通用模块来说,这样做肯定是不好的。下面我们看看promise可以做些什么。

exports.init = function (content,style,buttonConfirm,buttonCancel) {
        var dtd = $.Deferred();   // 创建一个deferred对象
        $('body').append(tpl);
        /* 中间代码省略 */
pop.find('#survey-confirm-y').click(function() {
    pop.remove();
dtd.resolve(); // 将deferred对象的执行状态修改为已完成
});
pop.find('#survey-confirm-n').click(function() {
    pop.remove();
dtd.reject(); // 将deferred对象的执行状态修改为失败
});
return dtd.promise(); // 返回另一个deferred对象
    };

首先,我们在init函数中定义一个deferred对象dtd,然后分别在两个按钮的点击事件中调用deferred.resolve()和deferred.reject()方法。Deferred对象有三种状态,进行中,已完成和已失败。而这两个方法就分别为将deferred对象的状态修改为已完成和已失败。当deferred对象的执行状态发生变化后,会触发相应的回调方法,后面会讲到。而init方法会在最后执行deferred.promise()方法来返回另一个deferred对象,也即是我们说的promise。这个promise和原本的deferred对象不同,它不具有对deferred状态进行修改的方法(我们只用promise执行状态改变后的回调)。页面上的弹窗调用代码如下所示:

$('#sea1').click(function(){
    seajs.use('sample', function (sa) {
    sa.init('this is a sample', null, 'Confirm', 'Cancel')
.done(function(){  // 已完成状态触发调用done
            seajs.use('sample', function (sa) {
            sa.init('this is another sample', null, 'Yes', 'No, Thx');
            });	
        }).fail(function(){  // 已失败状态触发调用fail
            seajs.use('sample', function (sa) {
            sa.init('this is a sample', null, null, 'ok');
            });
});
    });
});

init方法调用后返回promise(也即是deferred对象),然后可以通过deferred.done(callback)和deferred.fail(callback)方法来指定deferred对象状态改变后的回调。示例代码中实现的则是已完成状态时调用弹窗b,而已失败状态时调用弹窗c。除了done和fail,还可以用deferred.then(callbackDone, callbackFail)方法来分别指定成功和失败后的回调。而deferred还有一个方法deferred.always(callback),不论状态值变为失败或成功,都执行指定的回调。当需要等待多个deferred对象都改变状态才执行回调时,可以用jquery.when(dtd1, dtd2)方法。

 

写到这里,我们想要的这种灵活的弹窗已经duang地弹起来啦~

你可能感兴趣的:(【js模块化+promise】让弹窗们弹起来)