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 左右。

相关推荐
Манго нектар27 分钟前
JavaScript for循环语句
开发语言·前端·javascript
蒲公英100134 分钟前
vue3学习:axios输入城市名称查询该城市天气
前端·vue.js·学习
Zheng1132 小时前
【可视化大屏】将柱状图引入到html页面中
javascript·ajax·html
john_hjy2 小时前
【无标题】
javascript
软件开发技术深度爱好者3 小时前
用HTML5+CSS+JavaScript庆祝国庆
javascript·css·html5
汪子熙3 小时前
Angular 服务器端应用 ng-state tag 的作用介绍
前端·javascript·angular.js
杨荧3 小时前
【JAVA开源】基于Vue和SpringBoot的旅游管理系统
java·vue.js·spring boot·spring cloud·开源·旅游
一 乐8 小时前
学籍管理平台|在线学籍管理平台系统|基于Springboot+VUE的在线学籍管理平台系统设计与实现(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·学习
昨天;明天。今天。9 小时前
案例-表白墙简单实现
前端·javascript·css
安冬的码畜日常9 小时前
【玩转 JS 函数式编程_006】2.2 小试牛刀:用函数式编程(FP)实现事件只触发一次
开发语言·前端·javascript·函数式编程·tdd·fp·jasmine