美文网首页
弹窗组件由浅入深

弹窗组件由浅入深

作者: 苏敏 | 来源:发表于2018-01-31 10:12 被阅读86次

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

    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属性,需要手动指回来。这里仅仅是抛砖引玉,希望你能了解思路后写出更加高可用的组件,预计后期还会写级联组件、分页器组件和上传文件等组件,难度也是逐渐增加啊,继续加油,晚安 ~

    相关文章

      网友评论

          本文标题:弹窗组件由浅入深

          本文链接:https://www.haomeiwen.com/subject/vxytaxtx.html