1.在项目开发中,会选择一个组件库来提高我们的开发效率,在目前的项目中我们使用的是element-ui组件库,而element中的组件通常不能满足我们的业务需求,这时候我们就需要自己封装一个组件,这次遇到的一个需求如下图
这里点击版本和渲染问题,在网上搜寻一番尝试无果后,我选择自己封装一个下拉组件去完成
<template>
<div class="aop_endcomps_comps_select">
<div class="selectWrap">
<div class="select-wrapper">
<div class="select" @click="triggerOption">
<div class="select-content" style="font-size: 14px">
<span
:class="{
optionSpanActive:
(subject[0] && selectContent.status == '01') ||
selectContent.status == '',
optionSpan: subject[0] && selectContent.status == '02',
}"
style="font-size: 12px"
v-if="subject[0]"
>{{ selectContent.status == "01" ? "已发布" : "待发布" }}</span
>
{{ selectContent.version ? selectContent.version : "无版本管理" }}
</div>
<div class="triangle-wrapper">
<img src="~@m/assets/images/icon_下拉黑色.png" alt="" />
</div>
</div>
<div class="option-wrapper" style="display: none" v-show="this.showSelect.a">
<!-- 渲染父组件传来的值 -->
<div
class="option-item"
v-for="(item, index) in list"
:key="index"
@click="choose(item)"
>
<div style="font-size: 14px">
<span
:class="{
optionSpanActive: item.status == '01' || item.status == '',
optionSpan: item.status == '02',
}"
style="font-size: 12px"
>{{ item.status == "01" ? "已发布" : "待发布" }}</span
>
{{ item.version ? item.version : "" }}
</div>
</div>
<div class="optionFooter" @click="$emit('toVersion')">
组件库版本管理
</div>
</div>
</div>
</div>
</div>
</template>
methods: {
//深拷贝
deepClone(obj) {
let objClone = Array.isArray(obj) ? [] : {};
if (obj && typeof obj === "object") {
for (let key in obj) {
if (obj[key] && typeof obj[key] === "object") {
//判断对象的这条属性是否为对象
objClone[key] = this.deepClone(obj[key]); //若是对象进行嵌套调用
} else {
objClone[key] = obj[key];
}
}
}
return objClone; //返回深度克隆后的对象
},
//处理数据函数
getList(val) {
this.arr = this.deepClone(val);
if (this.arr.length == 1) {
for (let i = 0; i < this.arr.length; i++) {
this.list.push(this.arr[i]);
}
} else {
for (let i = 0; i < this.arr.length; i++) {
this.list.push(this.arr[i]);
}
}
// this.list = this.arr.splice(0,1);
console.log(this.list);
},
triggerOption() {
if (!this.subject[0]) {
return;
} else {
if (this.optionWrapper.style.display == "none") {
this.showSelect.a = true
this.optionWrapper.style.display = "block";
} else {
this.showSelect.a = true
this.optionWrapper.style.display = "none";
}
}
},
choose(item) {
if (!this.subject[0]) {
return;
} else {
if (this.arr.length == 1) {
this.list = [];
this.list = this.deepClone(this.arr);
} else {
// let idx = this.arr.findIndex((item1) => {
// return item.version === item1.version;
// });
// console.log(idx);
this.list = [];
this.list = this.deepClone(this.arr);
// this.list.splice(idx, 1);
}
this.selectContent.version = item.version;
this.selectContent.status = item.status;
this.optionWrapper.style.display = "none";
this.$emit("getReply", item);
}
},
},
组件封装完成,实现交互需求的时候发现两大问题:
第一个问题:需求是要求点击一个版本管理的时候显示当前点击的版本,然后下拉选择中的内容会剔除相应选择的一项,当时逻辑写出来以后发现自己选择之后会影响到父组件传来的值,并且选择之后非常混乱。
解决的方法是需要深拷贝传过来的数组,然后处理拷贝过后的数组,去遍历它渲染数据,而传过来的数组使用它当作模版。
第二个问题:需求是要求点击一个当前DOM的除了下拉选择组件的其他地方或者按esc键,下拉框会回收。
当时在网上搜了一种解决办法是这样的,在mounted里面给整体的页面文档绑定一个点击事件
然后用e.target判断是否点击的是组件内部的东西,如果不是传一个false的参数给组件,使组件的下拉框收回,但是这种情况就会有一个bug,就是当我点击菜单栏的其他按钮时会发生一些bug,这里bug没有记录,所以就不展示了,按esc键的时候也会有一些冲突。后面才知道可能是没有在destroyed里面删除这个事件,才触发的一系列bug。
还有一种解决方法,是在组件内部定义一个自定义方法如下:
directives: {
clickoutside: {
bind(el, binding, vnode) {
function documentHandler(e) {
if (el.contains(e.target)) {
return false;
}
if (binding.expression) {
binding.value(e);
}
}
function KeyUp(e) {
if (e.keyCode == 27) {
if (binding.expression) {
binding.value(e);
}
}
}
el.__vueClickOutSize__ = documentHandler;
el.__vueKeyup__ = KeyUp;
document.addEventListener("keyup", KeyUp);
document.addEventListener("click", documentHandler);
},
unbind(el, binding) {
document.removeEventListener("click", el.__vueClickOutSize__);
delete el.__vueClickOutSize__;
document.removeEventListener("keyup", el.__vueKeyup__);
delete el.__vueKeyup__;
},
},
},
使用的是el.contain()这个函数,判断是否点击了当前使用这个指令的DOM和判断是否按下了ESC键,而bind.value()则是v-指令绑定的函数,判断条件来调用这个函数,而el.__vueClickOutside__ = documentHandler;这段代码是为了方便在unbind删除绑定的这个事件,当删除这个事件以后这个el.__vueClickOutside__属性就不需要了,所以使用delete去删除这个属性
然后再当前组件使用,就可以解决当前的需求问题了。