el-table中 el-popover 性能优化

场景:在 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 左右。

相关推荐
香芋芋圆1 小时前
AI 冲击内卷之下,普通前端如何破局?WebGIS—— 低门槛突围赛道
前端·javascript·人工智能·学习·职场发展
INS_KF2 小时前
【编程笔记】成员函数中两个 const 的区别(const Data &getData() const;)
前端·javascript·笔记
宿6742 小时前
vue3-async
前端·javascript·vue.js
excel3 小时前
Nuxt + Twin CSS 中宽度超过屏幕时底部出现空白的原因与解决方案
前端·javascript
一个游离的指针6 小时前
函数管道:消除深度嵌套调用
前端·javascript
柚yuzumi7 小时前
前端优化,从少触发一次开始:防抖与节流
前端·javascript
默_笙7 小时前
🚤 CSS 布局的"圈地运动":BFC 就是浏览器的独立领地
前端·javascript
用户938515635078 小时前
大模型记忆指南:从短时缓存到永久记忆,手把手带你吃透 LangChain Memory 管理
javascript·人工智能
xqchen8 小时前
Web Components 普及困境深度解析:技术标准与工程实践的落差
javascript
爱丶不疚8 小时前
写给前端工程师的现代 Python 工程化最佳实践:从 pnpm 到 uv,从 CommonJS 到 src-layout
javascript·python·typescript