场景:在 el-table 中使用 el-popover ,出现了 loading 加载卡顿的问题,接口返回的数据的时间大概是 140ms ,所以不是接口慢的原因;通过对表中结构的逐步排查,发现是表中的 某一行 所影响的;并且 其中含有 el-popover;因为 el-popover 会渲染出真实的 dom 元素 所以在页面渲染的时候会出现el-table loading 卡顿的情况。
原来的代码是这样的
jsx
<ElTable v-loading="loading" :data="tableData" @selection-change="handleSelectionChange" size="small"
show-overflow-tooltip @row-dblclick="handleViewDetail" ref="tableRef"
:height="tableHeight" highlight-current-row @current-change="handleCurrentRowChange"
:row-class-name="tableRowClassName"
@sort-change="sort_change"
:cell-style="rowClassName"
:row-style="{height: '30px'}"
:header-cell-style="headerClassName"
>
//...
<el-table-column prop="remark" :label="$t('common.remark')" width="100">
<template #default="scope">
<el-popover :visible="scope.row.visible" placement="top" trigger="click" :width="204">
<el-input v-model="scope.row.remark" style="width: 180px;"
:placeholder="$t('common.email.setBlockSize')"/>
<div style="text-align: right; margin: 16px 0 0 0;">
<el-button size="small" text @click="() => {
scope.row.visible = false;
scope.row.remark = '';
}">{{ $t('common.sss16') }}
</el-button>
<el-button size="small" type="primary" @click="() => {
scope.row.visible = false;
setRemark(scope.row.mailId, scope.row.remark)
}"
>{{ $t('common.confirm') }}
</el-button
>
</div>
<template #reference>
<el-icon @click="scope.row.visible = true" :color="scope.row.remark ? '#40a9ff' : '#dddddd'">
<el-tooltip
v-if="scope.row.remark"
class="box-item"
:content="scope.row.remark"
placement="right"
>
<Memo/>
</el-tooltip>
<Memo v-else/>
</el-icon>
</template>
</el-popover>
</template>
</el-table-column>
//...
</ElTable>
解决办法:因为每次都要渲染真实dom;所以可以将 el-popover 抽离 就像 el-dialog 一样;只不过这里有特别的地方是------每行的数据都是不一样的,还需要动态展示每行的数据。
jsx
<el-table-column prop="remark" :label="$t('common.remark')" width="100">
<template #default="scope">
<el-icon :ref="(el) => (refMap[`${scope.row.id}`] = el)"
@click="handleRef(refMap[`${scope.row.id}`], scope.row)"
:color="scope.row.remark ? '#40a9ff' : '#dddddd'">
<el-tooltip
v-if="emailListCheckoutTarget.remark"
class="box-item"
:content="emailListCheckoutTarget.remark"
placement="right"
>
<Memo/>
</el-tooltip>
<Memo v-else/>
</el-icon>
</template>
</el-table-column>
抽离的 el-popover
jsx
<el-popover
virtual-triggering
:virtual-ref="tempRef"
v-model:visible="visiblePopover"
placement="top"
:width="204"
trigger="click"
:popper-options="{
modifiers: [{
name: 'offset',
options: {
offset: [8, 8]
}
}]
}">
<el-input v-model="emailListCheckoutTarget.remark" style="width: 180px;"
:placeholder="$t('common.email.setBlockSize')" @keydown.enter.native.stop="okPopover"/>
<div style="text-align: right; margin: 16px 0 0 0;">
<el-button size="small" text @click.stop="cancelPopover">{{ $t('common.sss16') }}
</el-button>
<el-button size="small" type="primary" @click.stop="okPopover"
>{{ $t('common.confirm') }}
</el-button
>
</div>
</el-popover>
最重要的一点是,采用这种方式,会出现 重复点击该列的目标对象的时候,会出现 visiblePopover 和 trigger 不同步的问题,表现为 el-popover 闪烁一次;所以需要在用户点击的时候重置 el-popover的显隐状态
jsx
//真实dom数组
const refMap = ref([])
//目标dom对象
const tempRef = ref(null)
//控制 el-popover 的显隐状态
const visiblePopover = ref(false)
//选中的行数据
const emailListCheckoutTarget = ref({})
//触发方法
const handleRef = (ref, item, type) => {
tempRef.value = ref
//重置 el-popover 显隐状态
visiblePopover.value = false;
setTimeout(() => {
visiblePopover.value = true;
}, 200)
emailListCheckoutTarget.value = item;
localStorage.setItem('targetItem', JSON.stringify(item.remark))
}
其次还要考虑到什么时候渲染指定的行内容;使用 鼠标 移入、移出 事件;
jsx
// 这里是开始点
const mouseEnters = throttle((row) => {
//localStorage.getItem("targetItem") 这里是特殊处理,可以根据实际情况处理
if (localStorage.getItem("targetItem") !== row.remark) {
visiblePopover.value = false
}
if (emailListCheckoutTarget.value.remark !== '') {
emailListCheckoutTarget.value = row;
}
}, 300)
const mouseLeaves = throttle((row) => {
if (localStorage.getItem("targetItem") === row.remark) {
// 防止popover 消失
visiblePopover.value = false;
}
}, 300)
这是两个方法:提交数据;取消提交
jsx
const cancelPopover = () => {
visiblePopover.value = false;
emailListCheckoutTarget.value.remark = ''
}
const okPopover = () => {
//这是提交到后端
setRemark(emailListCheckoutTarget.value.id, emailListCheckoutTarget.value.remark)
emailListCheckoutTarget.value = {};
visiblePopover.value = false;
}
经过上面的一顿操作后,肉眼可见的速度提高了,大约优化了 0.5s 左右。