当前位置: 首页 > article >正文

虚拟化表格(Virtualized Table)性能优化

文章目录

  • 功能介绍
  • 一开始的代码
  • 领导让我们分析一下
  • 开始优化
  • 如何监听事件和传参?
  • 定位操作栏
  • 更加优化

功能介绍

菜鸟最近做的一个功能如下:

后端返回两个很大的数组,例如:数组a 1w条,数组b 2w条,然后要操作b的数据去a里面,然后操作a的去b里面,最后把修改后的数组a和数组b返回给后端!且这个操作,是可以撤销的,用户操作了,但是没保存,是可以直接叉了,就不改后端数据!且数据还是可以搜索的!

一开始的代码

菜鸟一开始其实也考虑到了性能问题,但是当时是测试环境,最多就几十条数据,用 el-table 完全够用,且当时用 Virtualized Table 虚拟化表格 来渲染的时候老是 eslint 报错,所以当时就没管了!

接下来是老的代码

<script setup>
import { Search } from '@element-plus/icons-vue'

import { getExcelApi, saveReportInfoApi } from '@/network/analysisApi'

const props = defineProps({
  dialogVisible: {
    type: Boolean,
    default: false
  },
  id: {
    type: Number,
    default: -1
  }
})

const emit = defineEmits(['closeEvent'])

// 关闭弹窗
function handleClose() {
  emit('closeEvent', false)
}
const dialogBox = ref()
function closeDialog() {
  dialogBox.value.resetFields()
}

// 需要返回后端的数据
const subformData = {
  fileName: '',
  id: props.id,
  reportAPath: '',
  outputPath: ''
}
// 表数据
let reportA = ref([])
let reportB = ref([])
let oldreportA = []
let oldreportB = []
const loading = ref(true)
// 获取数据
getExcelApi(props.id)
  .then((res) => {
    console.log(res)
    if (res.code == 200) {
      subformData.fileName = res.data.fileName
      subformData.reportAPath = res.data.reportAPath
      subformData.outputPath = res.data.outputPath
      reportA.value = res.data.reportA
      reportB.value = res.data.reportB
      oldreportA = res.data.reportA
      oldreportB = res.data.reportB
      if (res.data.reportB.length <= 50 && res.data.reportA.length <= 50) {
        loading.value = false
      } else {
        setTimeout(() => {
          loading.value = false
        }, 5000)
      }
    } else {
      ElMessage({
        message: res.message,
        type: 'error'
      })
    }
  })
  .catch((err) => {
    console.log(err)
  })

// 搜索
let searchVal = ref('')
const search = () => {
  reportA.value = oldreportA.filter(function (i) {
    return i.patientName.includes(searchVal.value)
  })
  reportB.value = oldreportB.filter(function (i) {
    return i.patientName.includes(searchVal.value)
  })
}

// 表A减数据
const reduceFun = (e) => {
  let index = oldreportA.findIndex(
    (item) =>
      item.id === e.row.id &&
      item.barcode === e.row.barcode &&
      item.patientName === e.row.patientName &&
      item.species === e.row.species
  )
  let data = oldreportA.splice(index, 1)
  oldreportB.splice(0, 0, data[0])
  reportA.value = oldreportA.filter(function (i) {
    return i.patientName.includes(searchVal.value)
  })
  reportB.value = oldreportB.filter(function (i) {
    return i.patientName.includes(searchVal.value)
  })
}

// 表B加数据
const addFun = (e) => {
  let index = oldreportB.findIndex(
    (item) =>
      item.id === e.row.id &&
      item.barcode === e.row.barcode &&
      item.patientName === e.row.patientName &&
      item.species === e.row.species
  )
  let data = oldreportB.splice(index, 1)
  oldreportA.splice(0, 0, data[0])
  reportA.value = oldreportA.filter(function (i) {
    return i.patientName.includes(searchVal.value)
  })
  reportB.value = oldreportB.filter(function (i) {
    return i.patientName.includes(searchVal.value)
  })
}

// 提交表单
const submit = () => {
  subformData.reportA = oldreportA
  subformData.reportB = oldreportB
  saveReportInfoApi(subformData)
    .then((res) => {
      console.log(res)
      if (res.code === 200) {
        ElMessage({
          message: '提交审核成功!',
          type: 'success'
        })
        handleClose()
      } else {
        ElMessage({
          message: res.message,
          type: 'error'
        })
      }
    })
    .catch((err) => {
      console.log(err)
    })
}

// 定义table的表头
const columns = [
  {
    title: '测序批次',
    dataKey: 'batch',
    width: 300
  },
  {
    title: 'barcode',
    dataKey: 'barcode',
    width: 100
  },
  {
    title: '患者姓名',
    dataKey: 'patientName',
    width: 100
  },
  {
    title: '体系',
    dataKey: 'structure',
    width: 350
  },
  {
    title: '样本编号',
    dataKey: 'sampleNum',
    width: 150
  },
  {
    title: '报告编号',
    dataKey: 'reportNum',
    width: 150
  },
  {
    title: '样本类型',
    dataKey: 'sampleType',
    width: 150
  },
  {
    title: '提取Reads总数',
    dataKey: 'extractReads',
    width: 140
  },
  {
    title: '样本比对总reads',
    dataKey: 'sampleContrastReads',
    width: 150
  },
  // 内参检出情况
  // 分类
  {
    title: 'Species',
    dataKey: 'species',
    width: 300
  },
  {
    title: '物种中文名',
    dataKey: 'speciesCn',
    width: 250
  },
  {
    title: '物种比对Reads数',
    dataKey: 'speciesContrastReads',
    width: 150
  },
  // 样本检出靶标数
  // 特异靶标数
  {
    title: '综合可信度',
    dataKey: 'credibility',
    width: 150
  },
  {
    title: 'DNC的Reads数',
    dataKey: 'dncReads',
    width: 150
  },
  {
    title: '样本质控总reads',
    dataKey: 'qualityReads',
    width: 150
  },
  // DNC的靶标数
  {
    title: '同批最大reads数',
    dataKey: 'maxReads',
    width: 150
  },
  // 同批最高bc
  // 同批最高核酸编号
  {
    title: '物种类别',
    dataKey: 'speciesCategory',
    width: 150
  },
  {
    title: '定植情况',
    dataKey: 'planting',
    width: 400
  },
  {
    title: '结果解释',
    dataKey: 'resultExplain',
    width: 800
  },
  {
    title: '物种所在盘',
    dataKey: 'speciesDisk',
    width: 250
  },
  // 表中没有可能要修改
  {
    title: 'Genus',
    dataKey: 'genus',
    width: 150
  },
  {
    title: '属名',
    dataKey: 'genericName',
    width: 150
  },
  {
    title: '危害程度分类',
    dataKey: 'harm',
    width: 150
  },
  {
    title: '检出数/10000',
    dataKey: 'detectionNumber',
    width: 150
  },
  {
    title: '单样本Score',
    dataKey: 'sampleScore',
    width: 150
  }
]
</script>

<template>
  <div>
    <el-dialog
      title="结果筛选"
      ref="dialogBox"
      :modelValue="dialogVisible"
      :before-close="handleClose"
      @close="closeDialog"
      width="90%"
      top="30px"
      :close-on-click-modal="false"
      :destroy-on-close="true"
    >
      <div style="display: flex; width: 300px">
        <el-input v-model="searchVal" placeholder="患者姓名" clearable></el-input>
        <el-button style="margin-left: 50px" type="primary" :icon="Search" @click="search"
          >搜索</el-button
        >
      </div>
      <hr />
      <p>表格A</p>
      <div style="height: 300px">
        <el-table
          v-loading="loading"
          element-loading-text="加载中..."
          :data="reportA"
          style="width: 100%; height: 100%"
        >
          <template v-for="(item, index) in columns" :key="index">
            <el-table-column :prop="item.dataKey" :label="item.title" :width="item.width" />
          </template>
          <el-table-column fixed="right" label="操作" width="80" center>
            <template #default="scope">
              <el-button type="primary" size="small" @click="reduceFun(scope)"> - </el-button>
            </template>
          </el-table-column>
        </el-table>
      </div>
      <hr />
      <p>表格B</p>
      <div style="height: 300px">
        <el-table
          v-loading="loading"
          element-loading-text="加载中..."
          :data="reportB"
          style="width: 100%; height: 100%"
        >
          <template v-for="(item, index) in columns" :key="index">
            <el-table-column :prop="item.dataKey" :label="item.title" :width="item.width" />
          </template>
          <el-table-column fixed="right" label="操作" width="80" center>
            <template #default="scope">
              <el-button type="primary" size="small" @click="addFun(scope)"> + </el-button>
            </template>
          </el-table-column>
        </el-table>
      </div>
      <template #footer>
        <div>
          <el-button type="primary" @click="submit">提交</el-button>
          <el-button @click="handleClose">关闭</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>

但是这段代码在生产环境中就完全不够看了,生产环境不管是reportA还是reportB都是几千条左右,即使1秒就获取到了后端数据,但是 el-table 加载就要几秒钟,所以菜鸟直接写了一个5秒的定时器,等5秒后差不多渲染完了才把蒙层关闭(有点掩耳盗铃的感觉)!

重点是当菜鸟滚动列表的时候,那叫一个卡顿,且如果进行了移动数据的操作,那又会一卡一卡的,如果加上搜索,卡顿得让人难以想象!

领导让我们分析一下

卡成这样,用户肯定是受不了,所以领导就找我们分析原因!

菜鸟感觉前端数据量有点大,不如:用分页搜索并配合后端一起解决!但是很快被后端否决了,因为很麻烦,例如:

a查了10条,b查了10条,操作了b的一条去了a,那a点击第二页应该就是9-19条,而不是之前的10-20条,b也会变成1-11条(去掉操作的那一条),而不是1-10条了!

每一个操作都要向后端去请求,并告诉后端,数组a增加了哪一个、减少了哪一个、数组b增加了哪一个、减少了哪一个,交给后端去处理分页(并不改数组库)!

显然上面的这个思路得上千万条数据可能会使用的,菜鸟这个还不至于!

所以将思路定为前端性能优化,领导直接发话:这优化不了就该优化菜鸟我了!
在这里插入图片描述

开始优化

既然分到自己头上了,那就只能干了!奥里给!

菜鸟想起来了之前的 Virtualized Table 虚拟化表格 ,当时使用确实会报错,但是把那个报错的代码删除,确实反应很快,只是当时没有深究如何解决报错,想着偷懒去了,现在就硬上了!

所以代码改成了这样:

<script setup>
import { Search } from '@element-plus/icons-vue'

import { getExcelApi, saveReportInfoApi } from '@/network/analysisApi'

const props = defineProps({
  dialogVisible: {
    type: Boolean,
    default: false
  },
  id: {
    type: Number,
    default: -1
  }
})

const emit = defineEmits(['closeEvent'])

// 关闭弹窗
function handleClose() {
  emit('closeEvent', false)
}
const dialogBox = ref()
function closeDialog() {
  dialogBox.value.resetFields()
}

// 需要返回后端的数据
const subformData = {
  fileName: '',
  id: props.id,
  reportAPath: '',
  outputPath: ''
}
// 表数据
let reportA = ref([])
let reportB = ref([])
// 保存表头 --》 操作columns
let reportATitle = []
let reportBTitle = []
const columnsA = ref([])
const columnsB = ref([])
// 用于提交的数据
let oldreportA = []
let oldreportB = []
// 加载
const loading = ref(true)
// 获取数据
getExcelApi(props.id)
  .then((res) => {
    console.log(res)
    if (res.code == 200) {
      subformData.fileName = res.data.fileName
      subformData.reportAPath = res.data.reportAPath
      subformData.outputPath = res.data.outputPath
      reportA.value = res.data.reportA
      const tempA = reportA.value[0]
      for (let i in tempA) {
        console.log(i)
        if (i == 'id') continue
        reportATitle.push(i)
      }
      for (let i in reportATitle) {
        columnsA.value.push({
          title: reportATitle[i],
          key: reportATitle[i],
          dataKey: reportATitle[i],
          width: reportATitle[i] === '结果解释' || reportATitle[i] === '描述' ? 1200 : 200
        })
      }
      columnsA.value.push({
        key: 'operations',
        title: '操作',
        cellRenderer: () => (
          <>
            <ElButton type="primary" size="small">
              -
            </ElButton>
          </>
        ),
        width: 80,
        align: 'center'
      })
      reportB.value = res.data.reportB
      const tempB = reportB.value[0]
      for (let i in tempB) {
        if (i == 'id') continue
        reportBTitle.push(i)
      }
      for (let i in reportBTitle) {
        columnsB.value.push({
          title: reportBTitle[i],
          key: reportBTitle[i],
          dataKey: reportBTitle[i],
          width: reportBTitle[i] === '结果解释' || reportBTitle[i] === '描述' ? 1200 : 200
        })
      }
      columnsB.value.push({
        key: 'operations',
        title: '操作',
        cellRenderer: () => (
          <>
            <ElButton type="primary" size="small">
              +
            </ElButton>
          </>
        ),
        width: 80,
        align: 'center'
      })
      oldreportA = res.data.reportA
      oldreportB = res.data.reportB

      loading.value = false
    } else {
      ElMessage({
        message: res.message,
        type: 'error'
      })
    }
  })
  .catch((err) => {
    console.log(err)
  })

// 搜索
let searchVal = ref('')
const search = () => {
  reportA.value = oldreportA.filter(function (i) {
    return i['患者姓名'].includes(searchVal.value)
  })
  reportB.value = oldreportB.filter(function (i) {
    return i['患者姓名'].includes(searchVal.value)
  })
}

const uniqueItem = ['id', 'Barcode', '患者姓名', 'Species']
// 表A减数据
const reduceFun = (e) => {
  console.log(e)
  let index = oldreportA.findIndex((item) => {
    console.log(item)
    let num = 0
    for (let i of uniqueItem) {
      if (item[i] === e[i]) {
        num++
      }
    }
    return num === 4
  })
  let data = oldreportA.splice(index, 1)
  oldreportB.splice(0, 0, data[0])
  reportA.value = oldreportA.filter(function (i) {
    return i['患者姓名'].includes(searchVal.value)
  })
  reportB.value = oldreportB.filter(function (i) {
    return i['患者姓名'].includes(searchVal.value)
  })
}

// 表B加数据
const addFun = (e) => {
  console.log(e)
  let index = oldreportB.findIndex((item) => {
    console.log(item)
    let num = 0
    for (let i of uniqueItem) {
      if (item[i] === e[i]) {
        num++
      }
    }
    return num === 4
  })
  let data = oldreportB.splice(index, 1)
  oldreportA.splice(0, 0, data[0])
  reportA.value = oldreportA.filter(function (i) {
    return i['患者姓名'].includes(searchVal.value)
  })
  reportB.value = oldreportB.filter(function (i) {
    return i['患者姓名'].includes(searchVal.value)
  })
}

// 提交表单
const submit = () => {
  subformData.reportA = oldreportA
  subformData.reportB = oldreportB
  saveReportInfoApi(subformData)
    .then((res) => {
      console.log(res)
      if (res.code === 200) {
        ElMessage({
          message: '提交审核成功!',
          type: 'success'
        })
        handleClose()
      } else {
        ElMessage({
          message: res.message,
          type: 'error'
        })
      }
    })
    .catch((err) => {
      console.log(err)
    })
}
</script>

<template>
  <div>
    <el-dialog
      title="结果筛选"
      ref="dialogBox"
      :modelValue="dialogVisible"
      :before-close="handleClose"
      @close="closeDialog"
      width="90%"
      top="30px"
      :close-on-click-modal="false"
      :destroy-on-close="true"
    >
      <div style="display: flex; width: 300px">
        <el-input v-model="searchVal" placeholder="患者姓名" clearable></el-input>
        <el-button style="margin-left: 50px" type="primary" :icon="Search" @click="search"
          >搜索</el-button
        >
      </div>
      <hr />
      <p>表格A</p>
      <div style="height: 300px">
        <el-auto-resizer>
          <template #default="{ height, width }">
            <el-table-v2 :columns="columnsA" :data="reportA" :width="width" :height="height" fixed>
              <template #overlay v-if="loading">
                <div
                  class="el-loading-mask"
                  style="display: flex; align-items: center; justify-content: center"
                >
                  <el-icon class="is-loading">
                    <i-ep-loading />
                  </el-icon>
                </div>
              </template>
            </el-table-v2>
          </template>
        </el-auto-resizer>
      </div>
      <hr />
      <p>表格B</p>
      <div style="height: 300px">
        <el-auto-resizer>
          <template #default="{ height, width }">
            <el-table-v2 :columns="columnsB" :data="reportB" :width="width" :height="height" fixed>
              <template #overlay v-if="loading">
                <div
                  class="el-loading-mask"
                  style="display: flex; align-items: center; justify-content: center"
                >
                  <el-icon class="is-loading">
                    <i-ep-loading />
                  </el-icon>
                </div>
              </template>
            </el-table-v2>
          </template>
        </el-auto-resizer>
      </div>
      <template #footer>
        <div>
          <el-button type="primary" @click="submit">提交</el-button>
          <el-button @click="handleClose">关闭</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>

果然不出所料,还是报错:

Parsing error: Unexpected token <

在这里插入图片描述

这里菜鸟直接反手 ChatGPT,问了一个:

在这里插入图片描述

然后菜鸟就知道了,原来是jsx搞的鬼,知道了原因,解决就很快了,直接反手再来一发 ChatGPT :

在这里插入图片描述
在这里插入图片描述
然后配置一下 .eslintrc.cjs
在这里插入图片描述
配置好了之后发现没有报错,项目可以运行,自信满满点到这个界面,发现还是报错:
在这里插入图片描述
问了ChatGPT 发现是要在script标签上加个lang="jsx"

在这里插入图片描述

完美解决!

如何监听事件和传参?

这样渲染倒是上去了,但是jsx菜鸟不会呀,咋监听按钮的点击事件?咋传参数?

菜鸟只能一个一个尝试了!菜鸟想着都是 element 应该传值是一样的吧,所以变成了这样!

columnsA.value.push({
    key: 'operations',
    title: '操作',
    cellRenderer: (row) => (
      <>
        <ElButton type="primary" size="small" @click="addFun(row)">
          -
        </ElButton>
      </>
    ),
    width: 80,
    align: 'center'
})

但是直接报错

在这里插入图片描述

继续问 GPT:

在这里插入图片描述

然后菜鸟写成了这样

columnsA.value.push({
    key: 'operations',
    title: '操作',
    cellRenderer: (row) => (
      <>
        <ElButton type="primary" size="small" onClick="addFun(row)">
          -
        </ElButton>
      </>
    ),
    width: 80,
    align: 'center'
})

发现还是没有用,但是不报错了,果断 GPT:

在这里插入图片描述

最后代码成型:

<script lang="jsx" setup>
import { Search } from '@element-plus/icons-vue'

import { getExcelApi, saveReportInfoApi } from '@/network/analysisApi'

const props = defineProps({
  dialogVisible: {
    type: Boolean,
    default: false
  },
  id: {
    type: Number,
    default: -1
  }
})

const emit = defineEmits(['closeEvent'])

// 关闭弹窗
function handleClose() {
  emit('closeEvent', false)
}
const dialogBox = ref()
function closeDialog() {
  dialogBox.value.resetFields()
}

// 需要返回后端的数据
const subformData = {
  fileName: '',
  id: props.id,
  reportAPath: '',
  outputPath: ''
}
// 表数据
let reportA = ref([])
let reportB = ref([])
// 保存表头 --》 操作columns
let reportATitle = []
let reportBTitle = []
const columnsA = ref([])
const columnsB = ref([])
// 用于提交的数据
let oldreportA = []
let oldreportB = []
// 加载
const loading = ref(true)
// 获取数据
getExcelApi(props.id)
  .then((res) => {
    console.log(res)
    if (res.code == 200) {
      subformData.fileName = res.data.fileName
      subformData.reportAPath = res.data.reportAPath
      subformData.outputPath = res.data.outputPath
      reportA.value = res.data.reportA
      const tempA = reportA.value[0]
      for (let i in tempA) {
        console.log(i)
        if (i == 'id') continue
        reportATitle.push(i)
      }
      for (let i in reportATitle) {
        columnsA.value.push({
          title: reportATitle[i],
          key: reportATitle[i],
          dataKey: reportATitle[i],
          width: reportATitle[i] === '结果解释' || reportATitle[i] === '描述' ? 1200 : 200
        })
      }
      columnsA.value.push({
        key: 'operations',
        title: '操作',
        cellRenderer: (row) => (
          <>
            <ElButton type="primary" size="small" onClick={() => reduceFun(row.rowData)}>
              -
            </ElButton>
          </>
        ),
        width: 80,
        align: 'center'
      })
      reportB.value = res.data.reportB
      const tempB = reportB.value[0]
      for (let i in tempB) {
        if (i == 'id') continue
        reportBTitle.push(i)
      }
      for (let i in reportBTitle) {
        columnsB.value.push({
          title: reportBTitle[i],
          key: reportBTitle[i],
          dataKey: reportBTitle[i],
          width: reportBTitle[i] === '结果解释' || reportBTitle[i] === '描述' ? 1200 : 200
        })
      }
      columnsB.value.push({
        key: 'operations',
        title: '操作',
        cellRenderer: (row) => (
          <>
            <ElButton type="primary" size="small" onClick={() => addFun(row.rowData)}>
              +
            </ElButton>
          </>
        ),
        width: 80,
        align: 'center'
      })
      oldreportA = res.data.reportA
      oldreportB = res.data.reportB

      loading.value = false
    } else {
      ElMessage({
        message: res.message,
        type: 'error'
      })
    }
  })
  .catch((err) => {
    console.log(err)
  })

// 搜索
let searchVal = ref('')
const search = () => {
  reportA.value = oldreportA.filter(function (i) {
    return i['患者姓名'].includes(searchVal.value)
  })
  reportB.value = oldreportB.filter(function (i) {
    return i['患者姓名'].includes(searchVal.value)
  })
}

const uniqueItem = ['id', 'Barcode', '患者姓名', 'Species']
// 表A减数据
const reduceFun = (e) => {
  console.log(e)
  let index = oldreportA.findIndex((item) => {
    console.log(item)
    let num = 0
    for (let i of uniqueItem) {
      if (item[i] === e[i]) {
        num++
      }
    }
    return num === 4
  })
  let data = oldreportA.splice(index, 1)
  oldreportB.splice(0, 0, data[0])
  reportA.value = oldreportA.filter(function (i) {
    return i['患者姓名'].includes(searchVal.value)
  })
  reportB.value = oldreportB.filter(function (i) {
    return i['患者姓名'].includes(searchVal.value)
  })
}

// 表B加数据
const addFun = (e) => {
  console.log(e)
  let index = oldreportB.findIndex((item) => {
    console.log(item)
    let num = 0
    for (let i of uniqueItem) {
      if (item[i] === e[i]) {
        num++
      }
    }
    return num === 4
  })
  let data = oldreportB.splice(index, 1)
  oldreportA.splice(0, 0, data[0])
  reportA.value = oldreportA.filter(function (i) {
    return i['患者姓名'].includes(searchVal.value)
  })
  reportB.value = oldreportB.filter(function (i) {
    return i['患者姓名'].includes(searchVal.value)
  })
}

// 提交表单
const submit = () => {
  subformData.reportA = oldreportA
  subformData.reportB = oldreportB
  saveReportInfoApi(subformData)
    .then((res) => {
      console.log(res)
      if (res.code === 200) {
        ElMessage({
          message: '提交审核成功!',
          type: 'success'
        })
        handleClose()
      } else {
        ElMessage({
          message: res.message,
          type: 'error'
        })
      }
    })
    .catch((err) => {
      console.log(err)
    })
}
</script>

<template>
  <div>
    <el-dialog
      title="结果筛选"
      ref="dialogBox"
      :modelValue="dialogVisible"
      :before-close="handleClose"
      @close="closeDialog"
      width="90%"
      top="30px"
      :close-on-click-modal="false"
      :destroy-on-close="true"
    >
      <div style="display: flex; width: 300px">
        <el-input v-model="searchVal" placeholder="患者姓名" clearable></el-input>
        <el-button style="margin-left: 50px" type="primary" :icon="Search" @click="search"
          >搜索</el-button
        >
      </div>
      <hr />
      <p>表格A</p>
      <div style="height: 300px">
        <el-auto-resizer>
          <template #default="{ height, width }">
            <el-table-v2 :columns="columnsA" :data="reportA" :width="width" :height="height" fixed>
              <template #overlay v-if="loading">
                <div
                  class="el-loading-mask"
                  style="display: flex; align-items: center; justify-content: center"
                >
                  <el-icon class="is-loading">
                    <i-ep-loading />
                  </el-icon>
                </div>
              </template>
            </el-table-v2>
          </template>
        </el-auto-resizer>
      </div>
      <hr />
      <p>表格B</p>
      <div style="height: 300px">
        <el-auto-resizer>
          <template #default="{ height, width }">
            <el-table-v2 :columns="columnsB" :data="reportB" :width="width" :height="height" fixed>
              <template #overlay v-if="loading">
                <div
                  class="el-loading-mask"
                  style="display: flex; align-items: center; justify-content: center"
                >
                  <el-icon class="is-loading">
                    <i-ep-loading />
                  </el-icon>
                </div>
              </template>
            </el-table-v2>
          </template>
        </el-auto-resizer>
      </div>
      <template #footer>
        <div>
          <el-button type="primary" @click="submit">提交</el-button>
          <el-button @click="handleClose">关闭</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>

定位操作栏

但是这样了,菜鸟还是不满足,感觉一般这个操作都是要固定起来,而不是在最后一行看不见!

然后菜鸟又踩坑了,发现就是定位不了,最后对比官方网站找到原因,发现是需要从 element plus 里面引入 TableV2FixedDir

简化后的代码 (避免大家看很多,提出来这个部分了):

import { TableV2FixedDir } from 'element-plus'

columnsB.value.push({
    key: 'operations',
    title: '操作',
    cellRenderer: (row) => (
      <>
        <ElButton type="primary" size="small" onClick={() => addFun(row.rowData)}>
          +
        </ElButton>
      </>
    ),
    width: 80,
    align: 'center',
    fixed: TableV2FixedDir.RIGHT
})

更加优化

这个是菜鸟沸点底下的大佬给的思路:

在这里插入图片描述

所以这里把ref 换成 shallowRef

let reportA = shallowRef([])
let reportB = shallowRef([])

到此性能优化完成,可能对大佬来说真的很简单,但是菜鸟还是感觉有点成就感,毕竟性能提升了50倍以上!


http://www.kler.cn/a/403540.html

相关文章:

  • 《Object类》
  • 智能安全配电装置在高校实验室中的应用
  • 《人工智能深度学习的基本路线图》
  • 【Qt】QComboBox设置默认显示为空
  • 【前端知识】Javascript前端框架Vue入门
  • 【数据结构】【线性表】【练习】删除链表倒数第n个结点
  • Leetcode 快乐数
  • 安卓手机root+magisk安装证书+抓取https请求
  • C++ 类和对象(上)
  • 丹摩征文活动 | AI创新之路,DAMODEL助你一臂之力GPU
  • Java面试题2024-Java基础
  • el-scrollbar滚动表格时表头边框处有间隙的问题css
  • 火山引擎数据飞轮探索零售企业大促新场景:下放营销活动权限
  • Flutter:AnimatedContainer实现导航侧边栏
  • HBase Java基础操作
  • 网络是怎么连接的
  • uni-app跳转外部链接方式汇总--超全
  • 深度学习:位置前馈神经网络
  • HTML5实现剪刀石头布小游戏(附源码)
  • 将 FastAPI 部署到生产服务器(一套 全)
  • 基于Matlab的电力变压器建模方法(1):单相双绕组变压器的基本电路方程和仿真模型
  • Redisson 3.39.0 发布
  • React 中的Props特性及其应用
  • uniapp 购物弹窗组件 (微信小程序)
  • Jenkins下载安装、构建部署到linux远程启动运行
  • [免费]SpringBoot+Vue毕业设计论文管理系统【论文+源码+SQL脚本】