为什么存在el-form的页面被切换之后还在占用内存?

存在el-form的页面切换之后内存还在被占用,操作久了之后内存占用越来越大

一个vue3的两个页面都只含有el-form和el-table,无定时器和监听器,当操作久了之后发现内存一直在增长,然后去除掉table,发现只要含有el-form,页面频繁切换内存会增长且不会释放,不知道如何去释放这个被占用的内存,像echarts有dispose方法,但form没听说过,而且没有明白为什么form会造成内存一直占用。

补充问题,代码如下:
切换的页面就是InspectionReportVue、StatisticalReportVue、DefectReportVue这几个组件,这几个组件里只有form和table。另外index.vue也不是一个路由页面,它是嵌套在路由页面A里面的一个组件,同样也是被v-if来控制显示与否的。我不知道是不是我写的这个插槽有问题导致内存泄漏的

//index.vue
<template>
  <TableLayoutSelf title="XXX" :menuList="menuList">
    <template v-slot="data">
      <InspectionReportVue key="666" v-if="data.active === '1'" />
      <StatisticalReportVue key="777" v-if="data.active === '2'" />
      <DefectReportVue key="888" v-if="data.active === '3'" />
    </template>
  </TableLayoutSelf>
</template>

<script setup name="ReportForm">
import TableLayoutSelf from '../components/TableLayoutSelf/index.vue'
import InspectionReportVue from './inspectionReport.vue'
import StatisticalReportVue from './statisticalReport.vue'
import DefectReportVue from './defectReport.vue'
import nav1 from './images/nav1.png'
import nav2 from './images/nav2.png'
import nav3 from './images/nav3.png'

const menuList = ref([
  {
    id: '1',
    img: nav1,
    text: 'XXX',
  },
  {
    id: '2',
    img: nav2,
    text: 'XXX',
  },
  {
    id: '3',
    img: nav3,
    text: 'XXX',
  },
])
</script>
<style scoped lang="scss">
</style>
//TableLayoutSelf.vue
<template>
  <div class="layout">
    <div class="aside">
      <div class="title">{{ title }}</div>
      <div class="menu">
        <div class="item" v-for="item in menuList" :key="item.id" :id="item.id" @click="handleClick" :class="{'active': item.id === activeId}">
          <img :src="item.img" :id="item.id" />
          <span :id="item.id">{{ item.text }}</span>
        </div>
      </div>
    </div>
    <div class="content">
      <slot :active="activeId"></slot>
    </div>
  </div>
</template>

<script setup name="TableLayoutSelfSelf">
const props = defineProps({
  title: String,
  menuList: Array,
})

const { menuList } = toRefs(props)
const activeId = ref('1')
const handleClick = (e) => {
  activeId.value = e.target.id
}
</script>
<style scoped lang="scss">
.layout {
  display: flex;
  height: calc(100vh - vh(150));
  .aside {
    width: vw(240);
    flex-shrink: 0;
    .title {
      line-height: vh(55);
      text-align: center;
      background: #2d3e5b;
      color: #fff;
      font-size: vw(17);
    }
    .menu {
      .item {
        line-height: vh(76);
        border: 1px solid #22467e;
        display: flex;
        align-items: center;
        padding-left: vw(25);
        > img {
          width: vw(36);
          height: vh(38);
        }
        > span {
          margin-left: vw(25);
          font-size: vw(18);
          color: #fff;
        }
      }
      .item:hover,
      .active {
        background: linear-gradient(to right, #174a93, rgba(0, 0, 0, 0));
      }
      .item:not(:last-child) {
        border-bottom: 0;
      }
    }
  }
  .content {
    width: calc(100% - vw(240));
  }
}
</style>
//InspectionReport.vue
<template>
  <div class="table">
    <el-form class="search" :model="queryParams" ref="queryRef" v-show="showSearch" :inline="true" label-width="68px">
      <el-form-item label="设备类型" prop="deviceType">
        <el-select v-model="queryParams.deviceType" placeholder="设备类型" style="width: 200px" @change="getDeviceName">
          <el-option v-for="item in typeOptions" :key="item.value" :label="item.label" :value="item.value" />
        </el-select>
      </el-form-item>
      <el-form-item label="告警设备" prop="deviceName">
        <el-select v-model="queryParams.deviceName" placeholder="告警设备" style="width: 200px">
          <el-option v-for="item in deviceMap" :key="item.id" :label="item.name" :value="item.id" />
        </el-select>
      </el-form-item>
      <el-form-item label="任务状态" prop="status">
        <el-select v-model="queryParams.status" placeholder="任务状态" style="width: 200px">
          <el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
        </el-select>
      </el-form-item>
      <el-form-item label="任务开始时间" style="width: 458px" label-width="98px">
        <el-date-picker v-model="dateRange" type="datetimerange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD HH:mm:ss" :clearable="false"></el-date-picker>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
        <el-button icon="Refresh" @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>
    <div class="content">
      <el-row :gutter="10" class="mb8">
        <el-col :span="1.5">
          <el-button type="primary" icon="Download" @click="handleExport" :loading="btnload">导出</el-button>
        </el-col>
        <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
      </el-row>
      <el-table v-loading="loading" :data="list">
        <el-table-column label="设备" align="center" prop="deviceName" />
        <el-table-column label="巡检任务" align="center" prop="task_name" :show-overflow-tooltip="true" />
        <el-table-column label="巡检状态" align="center" prop="status">
          <template #default="scope">
            <dict-tag :options="taskStatus" :value="scope.row.status" />
          </template>
        </el-table-column>
        <el-table-column label="开始时间" align="center" prop="task_begin_time" width="180" />
        <el-table-column label="结束时间" align="center" prop="task_end_time" width="180" />
        <el-table-column label="持续时长" align="center" prop="timeCount" :show-overflow-tooltip="true" />
        <el-table-column label="报告生成时间" align="center" prop="roportCreateTime" :show-overflow-tooltip="true" />
        <el-table-column label="巡检点个数" align="center" prop="pointSum" />
        <el-table-column label="缺陷个数" align="center" prop="quxianSum" />
        <el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
          <template #default="scope">
            <el-button link type="primary" @click="handleDownload(scope.row)">下载</el-button>
          </template>
        </el-table-column>
      </el-table>

      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
    </div>
  </div>
</template>

<script setup name="InspectionReport">
import dayjs from 'dayjs'
import { saveAs } from 'file-saver'
import { ElMessageBox } from 'element-plus'
import { queryDeviceName, inspectList, exportInspect, downloadExcel, getHttpServerRoportAddr } from '@/api/control/monitor/reportForm'
const { proxy } = getCurrentInstance()
const { taskStatus } = proxy.useDict('taskStatus')
const showSearch = ref(true)
const list = ref([])
const loading = ref(false)
const total = ref(0)
const dateRange = ref([dayjs().subtract(1, 'day').format('YYYY-MM-DD 00:00:00'), dayjs().format('YYYY-MM-DD 23:59:59')])
const deviceMap = ref([]) //告警设备列表
const btnload = ref(false) //导出按钮加载中

const data = reactive({
  form: {},
  queryParams: {
    pageNum: 1,
    pageSize: 10,
    deviceType: '0',
    deviceName: undefined,
    status: '',
  },
})

const { queryParams, form } = toRefs(data)

const typeOptions = [
  {
    value: '0',
    label: '机器人',
  },
  {
    value: '6',
    label: '定点摄像头',
  },
]

const statusOptions = [
  {
    value: '',
    label: '所有',
  },
  {
    value: '2',
    label: '已执行',
  },
  {
    value: '3',
    label: '取消',
  },
]

onMounted(() => {
  getDeviceName('init')
})

const getDeviceName = (flag) => {
  queryDeviceName({ deviceType: queryParams.value.deviceType }).then((res) => {
    if (res.code === 200) {
      deviceMap.value = res.deviceNameMapList
      queryParams.value.deviceName = deviceMap.value[0]?.id
      if (flag === 'init') {
        getList()
      }
    }
  })
}

const getList = () => {
  loading.value = true
  inspectList(proxy.addDateRange(queryParams.value, dateRange.value))
    .then((res) => {
      if (res.code === 200) {
        list.value = res.rows
        total.value = res.total
      }
    })
    .finally(() => {
      loading.value = false
    })
}

const handleExport = () => {
  ElMessageBox.confirm('确定导出所有巡检报告吗?', '系统提示', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
  }).then(() => {
    btnload.value = true
    exportInspect(proxy.addDateRange(queryParams.value, dateRange.value)).then((res) => {
      if (res.code === 200) {
        downloadExcel({ fileName: res.msg }).then((data) => {
          btnload.value = false
          const blob = new Blob([data])
          saveAs(blob, res.msg)
        })
      }
    })
  })
}

const handleDownload = (row) => {
  const { device_id, report_url } = row
  if (report_url.indexOf('http:') == -1) {
    getHttpServerRoportAddr({ robotId: device_id }).then((res) => {
      if (res.code === 200) {
        const url = `http://${res.http_server_addr}/${report_url}`
        window.open(url)
      }
    })
  } else {
    window.open(report_url)
  }
}

const handleQuery = () => {
  queryParams.value.pageNum = 1
  getList()
}

const resetQuery = () => {
  proxy.resetForm('queryRef')
  dateRange.value = [dayjs().subtract(1, 'day').format('YYYY-MM-DD 00:00:00'), dayjs().format('YYYY-MM-DD 23:59:59')]
  getDeviceName('init')
}
</script>
<style scoped lang="scss">
.table {
  background: #f3f3f4;
  width: 100%;
  height: 100%;
  padding: vh(10) vw(20);
  overflow: auto;
  .search,
  .content {
    background: #fff;
    border-radius: 6px;
    box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.2);
  }
  .search {
    padding: vh(20) vw(20) 0;
    margin-bottom: vh(15);
  }
  .content {
    padding: vh(20) vw(20);
  }
}
</style>

快照图片如下:存在增量,但是不知道如何通过这个去分析定位具体代码
image.png
image.png
image.png
image.png
image.png

阅读 2.3k
2 个回答

先看看切换页面有没有使用<keep-alive>
如果无从入手,可以直接用开发者工具拍几个内存快照对比一下切换页面前后的内存变化,直接追踪堆中的对象引用

看补充的业务代码,没有问题。但是不知道你的 el-form 里面是怎么样的。比如说是否有一些下拉选择或者树形控件之类的,可能会有比较大数据列表的组件。
有没有合理清空对应的数据导致的溢出?比如说下拉数据添加到 pinia 这种状态库中了,有没有清理掉上一次的旧数据。

做一下具体的分析吧,看看原因是 el-form 组件导致的,还是其他的原因。比如说上面举例的数据没有清理引起的。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏