弹窗组件由浅入深

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

Alert弹窗组件预览

image.png

Login弹窗组件预览

image.png

需求分析

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

通用弹窗思路

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

// 定义父容器
let html = `
`;

通过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 = `
`;

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

你可能感兴趣的:(弹窗组件由浅入深)