弹窗是一种通用性极强的页面操作,无论是用户的登录注册还是提示信息都会用到我们要说的弹窗,所以对于各种各样的弹窗我们该怎么去复用呢?首先我们需要把弹窗组件化,每个需要用到弹窗的页面调用该组件就可以了,这样还不够,我们还需要通过继承组件来达到定制化弹窗开发,这也是我们今天要说的。
Alert弹窗组件预览
Login弹窗组件预览
需求分析
通用弹窗组件作为父组件提供给其他具体业务弹窗组件继承
通用弹窗提供基本的边框样式和隐藏显示功能
业务弹窗则只需要定制自己需要的内容和功能即可
继承自定义的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 = `
Alert Information
`;
首先传入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 的关系图为:
使用 extend 的关系图为:
使用 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属性,需要手动指回来。这里仅仅是抛砖引玉,希望你能了解思路后写出更加高可用的组件,预计后期还会写级联组件、分页器组件和上传文件等组件,难度也是逐渐增加啊,继续加油,晚安 ~