弹窗组件由浅入深

弹窗是一种通用性极强的页面操作,无论是用户的登录注册还是提示信息都会用到我们要说的弹窗,所以对于各种各样的弹窗我们该怎么去复用呢?首先我们需要把弹窗组件化,每个需要用到弹窗的页面调用该组件就可以了,这样还不够,我们还需要通过继承组件来达到定制化弹窗开发,这也是我们今天要说的。

Alert弹窗组件预览

image.png

Login弹窗组件预览

image.png

需求分析

通用弹窗组件作为父组件提供给其他具体业务弹窗组件继承
通用弹窗提供基本的边框样式和隐藏显示功能
业务弹窗则只需要定制自己需要的内容和功能即可
继承自定义的alert弹窗和登录弹窗

通用弹窗思路

父组件一般提供通用性的功能即可,所以这里考虑到alert弹窗和登录弹窗的相同点和不同点,我们给通用弹窗定义一个容器以及边框和水平垂直居中,然后右上角需要一个关闭的button,提供隐藏和显示弹窗的方法,其实挺简单的吧 ~

// 定义父容器
let html = `<div class="m-modal"></div>`;

通过extend方法拿到传入的参数,并将html转成node方便操作,然后将节点渲染到页面上即可

function Modal(option) {
    // 继承配置
    _.extend(this, option);
    // 缓存节点
    this.container = _.html2node(html);
    this.container.innerHTML = this.content || '';
    this.parent.appendChild(this.container);
}

提供显示和隐藏弹窗的方法

show: function() {
    _.delClassName(this.container, 'f-dn');
},

hide: function() {
    _.addClassName(this.container, 'f-dn');
    this.emit('hide');
},

到这里我们通用组件就完成了,接下来看定制化的alert弹窗组件吧

// alert弹窗html结构
let html = `<div class="m-alertmodal">
        <span class="close_btn u-icon u-icon-close"></span>
        <div class="modal_tt">
            <strong>Alert Information</strong>
        </div>
        <div class="modal_ct">
            <p class="alert_msg"></p>
            <button class="u-btn u-btn-primary submit_btn">确定</button>
        </div>
    </div>`;

首先传入html节点到 this.content ,通过原型式继承得到 AlertModal.prototype ,这里要多谢冴羽大神帮我分析原型是继承和浅拷贝的困惑,这里可以看一下,当做笔记记在小本本里。

function AlertModal(option) {
    // 传入content
    this.content = html;

    // 借调构造函数实现继承
    Modal.call(this, option);

    // 缓存节点
    this.alertMsg = this.container.querySelector('.alert_msg');
    this.submitBtn = this.container.querySelector('.submit_btn');
    this.closeBtn = this.container.querySelector('.close_btn');

    this.initModal();
}

// 通过原型式继承会导致AlertModal.prototype.constructor === Modal,需要手动指回去
AlertModal.prototype = Object.create(Modal.prototype);

这里也贴一下extend的代码:

// 属性赋值,通过浅拷贝
_.extend = function(o1, o2) {
    for (var i in o2) if (typeof o1[i] === 'undefined') {
        o1[i] = o2[i];
    }
    return o1;
};

使用 Object.create 的关系图为:


image.png

使用 extend 的关系图为:


image.png

使用 extend,我们仅仅是给 AlertModal.prototype 添加各种方法而已,就类似于 AlertModal.prototype.a = fn;
如果非要说区别的话,比如我给 Modal.prototype 添加了一个函数, 使用 Object.create 实现继承的话,alertModal 可以自动获得这个方法,而使用 extend 的方式,必须要再执行一次 _.extend(AlertModal.prototype, Modal.prototype) 才能让 alertModal 获得该方法

在这里通过Object.create方式会使 AlertModal.prototype.constructor === Modal 指回去就好了~

接着往下看,我们需要初始化alertModal组件

初始化的时注册alert弹窗事件,显示自定义内容,然后绑定事件

_.extend(AlertModal.prototype, {
    initModal: function() {
        // 触发alert弹窗
        this.on('alert', this.showMsg.bind(this));

        _.addEvent(this.closeBtn, 'click', this.hide.bind(this));
        _.addEvent(this.submitBtn, 'click', this.success.bind(this));
    },

    showMsg: function(msg) {
        this.alertMsg.innerHTML = msg;
        this.show();
    }
});

在测试页面我们提供父容器节点和触发对应的事件即可,到这里我们完成了alert弹窗的继承组件开发的一般流程,实际中考虑的内容比这多很多,这里只是简化过程,明白实现弹窗并且怎么继承组件即可。

let alert = new AlertModal({
    parent: document.body,
});

// 触发事件
alert.emit('alert', '我是Alert弹窗');

alert.on('hide', function() {
    console.log('触发hide事件')
});
alert.on('success', function() {
    console.log('触发success事件')
})

登录弹窗实现

有了上面alert弹窗继承的完成后,登录弹窗的继承也就变得很简单,只需要改动几行代码就可以,重复的步骤就不讲了,这里看一看代码,首先还是通过继承得到父类的方法和属性

// 继承父类Modal的原型
LoginModal.prototype = Object.create(Modal.prototype);
LoginModal.prototype.constructor = LoginModal;

初始化时注册登录弹窗并绑定相应的事件,这里由于和注册弹窗有交互,所以这里不去实现

// 初始化登录弹窗组件并绑定事件
initLoginEvent: function() {
    this.on('showLoginModal', this.show.bind(this));

    _.addEvent(this.close, 'click', this.hide.bind(this));
    _.addEvent(this.goregister, 'click',
    function() {
        this.hide();
        // 此处不实现真实的注册弹窗
        this.emit('showRegisterModal');
    }.bind(this));
    _.addEvent(this.submit, 'click', this._submit.bind(this));
}

check表单验证方法是真实项目中用到的,通过正则验证用户名和密码是否为空以及是否符合具体要求,这里贴出来仅供参考。

// 检查用户名和密码是否为空以及长度判断
check: function() {
    let isValid = true,
    flag = true;

    // 验证用户名
    flag = _.isPhone(this.userName.value) && flag;
    flag = !_.isNotEmpty(this.userName.value) && flag;
    flag ? _.delClassName(this.userName, 'error') : _.addClassName(this.userName, 'error');
    isValid = isValid && flag;

    // 验证密码
    flag = true;
    flag = !_.isNotEmpty(this.password.value) && flag;
    flag = _.pwdLength(this.password.value) && flag;
    flag ? _.delClassName(this.password, 'error') : _.addClassName(this.password, 'error');
    isValid = isValid && flag;

    isValid || (this.nError.innerText = '账号或密码错误,请输入正确密码~');

    this.showError();

    isValid ? _.addClassName(this.ErrorParent, 'f-dn') : this.showError();

    return isValid;
},

实际应用中我们检测完成后会提交到服务器上,这里为了演示省略这一步,通过触发事件了解我们登录是正确的即可。

// 提交用户信息前先阻止默认事件
// 检查cookie状态,并设置选中‘保持登录’的失效时间
_submit: function(event) {
    let that = this;
    event.preventDefault();

    let user = {
        username: that.userName.value.trim(),
        password: hex_md5(that.password.value),
        remember: !!that.remember.checked
    };

    // 这里不做真实登录,仅做一下演示
    if (that.check()) {
        console.log(user);
        that.emit('showLoginModal');
        that.hide();
        // _.ajax({
        //     url: '/api/login',
        //     method: 'POST',
        //     data: user,
        //     success: function (data) {
        //         let dataOrz = JSON.parse(data);
        //         console.log(data)
        //         if (dataOrz.code === 200) {
        //             that.hide();
        //             that.emit('login', data.result);
        //             that.lastSuc();
        //             _.setCookie('loginSuc', 'loginSuc');
        //         } else {
        //             that.hide();
        //         }
        //     },
        //     fail: function () {}
        // })
    }
}

总结

至此,我们就算完成了通过继承的方式实现弹窗组件,注意到原型式继承会改变本身的constructor属性,需要手动指回来。这里仅仅是抛砖引玉,希望你能了解思路后写出更加高可用的组件,预计后期还会写级联组件、分页器组件和上传文件等组件,难度也是逐渐增加啊,继续加油,晚安 ~

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

推荐阅读更多精彩内容

  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,711评论 2 17
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,335评论 25 707
  • 上海的天气有年少记忆的味道 春末的香樟淅沥沥的雨还有此刻干燥明亮的风 所有的美好都让此刻的我觉得真实而惶恐 你如果...
    宁静Ning阅读 257评论 0 0
  • 嗨,boy。好久不见!最近过的好吗?听说你最近成了宅男,恋上了家。只是偶尔还会骑着小白去小镇理个发。 嗨,boy。...
    凌九七阅读 328评论 2 3
  • 这几天,“左先生、右先生”的故事一度霸屏。有人总结:谈恋爱要找左先生,结婚要找右先生。也有人说,右先生是成熟后的左...
    我本卿狂阅读 640评论 0 0