为什么要这样封装使用?原因是业务数据庞大,table表格的行数列数太多,同时table表格内套用过多 elemet ui 框架的【el-input】【el-tooltip】组件,导致渲染很慢,滚动会导致页面卡顿。Ant Design of Vue的ui项目也存在套用过多【Tooltip 文字提示】组件,导致渲染很慢。为了解决这个问题,就需要减少ui的组件套用(用html标签+css替换ui组件),可以提高渲染速度,同时还需要用虚拟table表格渲染动态渲染的数据。
【index.js】与【JTooltip.vue】文件的目录
image.png
在【main.js】引入
//文本提示工具
import JTooltip from '@/components/aocsas/JTooltip/index';
Vue.use(JTooltip);
image.png
【index.js】代码如下:
/**
在main.js中引入
import JTooltip from '@/components/aocsas/JTooltip/index';
Vue.use(JTooltip); //注册指令
* */
import JTooltip from "./JTooltip.vue";
const TOOLTIP = {
install(Vue) {
let MessageConstructor = Vue.extend(JTooltip);
//instance相当于Message.vue的this
let instance = new MessageConstructor();
instance.$mount();
document.body.appendChild(instance.$el);
Vue.nextTick(() => {});
//注册一个全局自定义指令 v-tooltip="'文本提示内容'"
Vue.directive("tooltip", {
bind(el, binding, vnode, oldVnode) {
el.$message = binding.value;
el.addEventListener("mouseover", function (e) {
e.stopPropagation(); //阻止冒泡
instance.show(el); //鼠标进入显示:传递dom标签元素
});
el.addEventListener("mouseout", function (e) {
instance.hide(); //鼠标离开隐藏
});
},
inserted() {},
update(el, binding, vnode, oldVnode) {
el.$message = binding.value; //将更新的数据绑定到dom标签元素上
if (binding.value !== binding.oldValue) {
instance.updateDate(); //更新数据
}
},
componentUpdated(el, binding, vnode, oldVnode) {},
unbind() {},
});
},
};
export default TOOLTIP;
【JTooltip.vue】代码如下:
<!--
为什么要这样封装使用?原因是ui框架的tooltip组件,套用过多的话,会导致页面卡顿
用法:
<div class="ellipsis1" v-tooltip="'文本提示内容'" width="250" placement="top">这是一个内容文本!</div>
<div class="ellipsis1" v-tooltip="'文本提示内容'" width="300" placement="bottom">这是一个内容文本!</div>
-->
<template>
<div
class="j-tooltip-layer"
:class="{ active: isActive }"
:style="{ ...getStyleObj, width: 'auto' }"
@mouseover="handleMouseover"
@mouseout="handleMouseout"
>
<div class="j-tooltip-arrow" :placement="placement" :style="{ left: arrowLeft }"></div>
<div class="j-tooltip-content" :style="{ width: getStyleObj.width }">{{ content }}</div>
</div>
</template>
<script>
export default {
name: "JTooltip",
components: {},
props: {},
watch: {},
computed: {
getStyleObj() {
this.rect = JSON.parse(JSON.stringify(this.rect));
if (Object.keys(this.rect).length === 0) return {};
let rect = this.rect;
let left = rect.left + rect.width / 2;
let top = rect.top - 12;
let { width, height } = this.getTextWidthHeight(this.content);
this.arrowLeft = "50%";
if (rect.left + rect.width / 2 + width / 2 > document.body.clientWidth) {
//在右边超出了
left = document.body.clientWidth - width / 2;
this.arrowLeft = `${width - (document.body.clientWidth - (rect.left + rect.width / 2)) - 2}px`;
} else if (rect.left + rect.width / 2 < width / 2) {
//在左边超出了
left = width / 2;
this.arrowLeft = `${rect.left + rect.width / 2}px`;
}
if (height > rect.top) {
//在上面超出了(被遮挡了)
top = rect.top + rect.height + height + 12;
this.placement = "bottom";
}
if (this.placement == "bottom") {
if (rect.top + rect.height + height > document.body.clientHeight) {
this.placement = "top";
} else {
top = rect.top + rect.height + height + 12;
}
}
return {
top: top + "px",
left: left + "px",
width: width + "px",
};
},
},
data() {
return {
isActive: false,
content: "",
maxWidth: 250,
rect: {},
domEl: {},
placement: "top",
arrowLeft: "",
};
},
mounted() {},
beforeDestroy() {},
methods: {
//鼠标移进来(显示)
show(domEl) {
this.domEl = domEl;
this.isActive = true;
this.content = this.domEl.$message;
this.rect = this.domEl.getBoundingClientRect(); //获取元素位置和尺寸
this.maxWidth = this.domEl.getAttribute("width") || 250;
this.placement = this.domEl.getAttribute("placement") || "top";
clearTimeout(this.timer);
},
//鼠标移出去(隐藏)
hide() {
clearTimeout(this.timer);
this.timer = setTimeout(() => {
this.isActive = false;
clearTimeout(this.timer);
}, 100);
},
//被指令周期函数调用
updateDate() {
this.content = this.domEl.$message;
this.rect = this.domEl.getBoundingClientRect();
},
//获取文字的宽度/高度
getTextWidthHeight(text = "") {
const dom = document.createElement("div");
dom.innerHTML = text;
dom.classList.add("j-tooltip-content");
dom.style.position = "absolute";
dom.style.top = "-999999px";
dom.style.whiteSpace = "nowrap"; //不换行计算出来的宽度是正确的
document.body.appendChild(dom);
let width = dom.offsetWidth + 2;
width = Math.min(this.maxWidth, width);
dom.style.whiteSpace = "normal"; //恢复默认值,高度才能展开
dom.style.width = width + "px";
dom.style.maxWidth = width + "px";
let height = dom.offsetHeight;
document.body.removeChild(dom);
return {
width: width, //增加18px是padding左右边距
height: height, //增加12px是padding上下边距,4px是箭头高度
};
},
handleMouseover() {
clearTimeout(this.timer);
},
handleMouseout() {
clearTimeout(this.timer);
this.timer = setTimeout(() => {
this.isActive = false;
clearTimeout(this.timer);
}, 100);
},
},
};
</script>
<style>
.j-tooltip-layer {
position: fixed;
top: 0px;
left: 0px;
z-index: 2147483647;
visibility: hidden;
opacity: 0;
transform: translate(-50%, -100%);
transition: opacity 0.3s;
}
.j-tooltip-layer.active {
opacity: 1;
visibility: visible;
}
.j-tooltip-content {
min-height: 32px;
padding: 6px 8px;
color: #fff;
text-align: left;
text-decoration: none;
display: flex;
justify-content: center;
background-color: rgba(0, 0, 0, 0.75);
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
box-sizing: border-box !important;
font-size: 14px;
word-break: break-all;
word-wrap: break-word;
white-space: normal;
line-height: normal;
}
.j-tooltip-arrow {
box-sizing: content-box !important;
position: absolute;
left: 50%;
transform: translateX(-50%);
display: block;
border-width: 4px;
border-style: solid;
border-color: transparent;
}
.j-tooltip-arrow[placement="top"] {
top: 100%;
border-top-color: rgba(0, 0, 0, 0.75);
}
.j-tooltip-arrow[placement="bottom"] {
bottom: 100%;
border-bottom-color: rgba(0, 0, 0, 0.75);
}
</style>
image.png
image.png
注意:如果是套用UI组件过多,导致渲染过慢,建议用纯div+css这种方式替换UI组件,渲染效率会有明显的提升!虚拟表格+原生的div+css解决了项目渲染过慢的问题!
虚拟表格用的是这个框架:
http://www.umyui.com/