前端佬们!塌房了!用过Element-Plus的进来~

进来着急的前端佬,我直接抛出结论吧!

Element-plus的组件,经过测验,如下组件存在内存泄漏。如下:

  • el-carousel
  • el-select + el-options
  • el-descriptions
  • el-tag
  • el-dialog
  • el-notification
  • el-loading
  • el-result
  • el-message
  • el-button
  • el-tabs
  • el-menu
  • el-popper

验证环境为:

Vue Version: 3.5.13

Element Plus Version: 2.9.7

Browser / OS: window 10 / Edge 134.0.3124.85 (正式版本) (64 位)

Build Tool: Webpack

不排查ElementUI也存在这个问题。

好了。接下来细细聊。

前因

为什么检测到这种问题?主要因为一个项目引用了Element-plus。然后,你懂的,买的人永远都会想要最好的,然后买的人就这么一顿狂点Web页面,看见内存占用飙到老高。

于是...前端佬都懂的,来活了。

排查

一开始我是不敢怀疑这种高star开源组件的。总以为自己是写的代码有问题。

详细代码就不贴了,主要用ElDialog组件,封装成一个命令式的Dialog组件,避免频繁的使用v-modal参数。

然后,就直接怀疑上这个组件了。

经过测试,果不其然,从关闭到销毁,会导致内存猛增,因为Dialog中有各种表单组件,一打开就创建了一大堆的Element元素。

精确定位,使用了FinalizationRegistry类追踪创建的Dialog实体,代码如下:

js 复制代码
const finalizerRegistry = new FinalizationRegistry((heldValue) => {
  console.log('Finalizing instance: ',heldValue);
});


// 在创建处监听
const heldValue = Symbol(`DialogCommandComponent_${Date.now()}`);
finalizerRegistry.register(this, heldValue);
console.log(`Constructed instance:`,heldValue);

发现一直没有Constructed instance销毁的信息输出。

随后,使用了Edge浏览器中的分离元素来打快照,步骤如下图。

经过反复的操作,然后点击主动垃圾回收,然后发现el-dialog的元素都会增加,基本确认无疑了。

但还是怀疑,会不会是Dialog中,引用的问题,导致元素一直没能销毁?所以,使用了纯纯的el-dialog来校验,同样的操作,既然如故。

然后的然后,我使用了如下的代码,去校验其它组件是否存在同样的问题。代码如下:

vue 复制代码
<template>
  <div>
    <el-button @click="fn2">Reset</el-button>
  </div>
  <el-dialog v-model="model" destroy-on-close @closed="fn1" append-to-body v-if="destroyDialogModelValue"></el-dialog>
  <el-button @click="fn0" v-if="!button" primse>Click</el-button>
  <div class="weak" v-if="!button">xxx</div>
  <el-input v-if="!button" />
  <el-border v-if="!button" />
  <el-select v-if="!button">
    <el-option>1111</el-option>
  </el-select>
  <el-switch v-if="!button" />
  <el-radio v-if="!button" />
  <el-rate v-if="!button" />
  <el-slider v-if="!button" />
  <el-time-picker v-if="!button" />
  <el-time-select v-if="!button" />
  <el-transfer v-if="!button" />
  <el-tree-select v-if="!button" />
  <el-calendar v-if="!button" />
  <el-card v-if="!button" />
  <el-carousel height="150px" v-if="!button">
    <el-carousel-item v-for="item in 4" :key="item">
      <h3 class="small justify-center" text="2xl">{{ item }}</h3>
    </el-carousel-item>
  </el-carousel>
  <el-descriptions title="User Info" v-if="!button">
    <el-descriptions-item label="Username">kooriookami</el-descriptions-item>
  </el-descriptions>
  <el-table style="width: 100%" v-if="!button">
    <el-table-column prop="date" label="Date" width="180" />
    <el-table-column prop="name" label="Name" width="180" />
    <el-table-column prop="address" label="Address" />
  </el-table>
  <el-avatar v-if="!button" />
  <el-pagination layout="prev, pager, next" :total="50" v-if="!button" />
  <el-progress :percentage="50" v-if="!button" />
  <el-result icon="success" title="Success Tip" sub-title="Please follow the instructions" v-if="!button">
    <template #extra>
      <el-button type="primary">Back</el-button>
    </template>
  </el-result>
  <el-skeleton v-if="!button" />
  <el-tag v-if="!button" />
  <el-timeline v-if="!button" />
  <el-tree v-if="!button" />
  <el-avatar v-if="!button" />
  <el-segmented size="large" v-if="!button" />
  <el-dropdown v-if="!button">
    <span class="el-dropdown-link">
      Dropdown List
      <el-icon class="el-icon--right">
        <arrow-down />
      </el-icon>
    </span>
    <template #dropdown>
      <el-dropdown-menu>
        <el-dropdown-item>Action 1</el-dropdown-item>
        <el-dropdown-item>Action 2</el-dropdown-item>
        <el-dropdown-item>Action 3</el-dropdown-item>
        <el-dropdown-item disabled>Action 4</el-dropdown-item>
        <el-dropdown-item divided>Action 5</el-dropdown-item>
      </el-dropdown-menu>
    </template>
  </el-dropdown>
  <el-menu class="el-menu-demo" mode="horizontal" v-if="!button">
    <el-menu-item index="1">Processing Center</el-menu-item>
    <el-sub-menu index="2">
      <template #title>Workspace</template>
      <el-menu-item index="2-1">item one</el-menu-item>
      <el-menu-item index="2-2">item two</el-menu-item>
      <el-menu-item index="2-3">item three</el-menu-item>
      <el-sub-menu index="2-4">
        <template #title>item four</template>
        <el-menu-item index="2-4-1">item one</el-menu-item>
        <el-menu-item index="2-4-2">item two</el-menu-item>
        <el-menu-item index="2-4-3">item three</el-menu-item>
      </el-sub-menu>
    </el-sub-menu>
    <el-menu-item index="3" disabled>Info</el-menu-item>
    <el-menu-item index="4">Orders</el-menu-item>
  </el-menu>

  <el-steps style="max-width: 600px" active="0" finish-status="success" v-if="!button">
    <el-step title="Step 1" />
    <el-step title="Step 2" />
    <el-step title="Step 3" />
  </el-steps>

  <el-tabs class="demo-tabs" v-if="!button">
    <el-tab-pane label="User" name="first">User</el-tab-pane>
    <el-tab-pane label="Config" name="second">Config</el-tab-pane>
    <el-tab-pane label="Role" name="third">Role</el-tab-pane>
    <el-tab-pane label="Task" name="fourth">Task</el-tab-pane>
  </el-tabs>

  <el-alert title="Success alert" type="success" v-if="!button" />
  <el-drawer title="I am the title" v-if="!button">
    <span>Hi, there!</span>
  </el-drawer>

  <div v-loading="model" v-if="!button"></div>

  <el-popconfirm confirm-button-text="Yes" cancel-button-text="No" icon-color="#626AEF"
    title="Are you sure to delete this?" v-if="!button">
    <template #reference>
      <el-button>Delete</el-button>
    </template>
  </el-popconfirm>

  <el-popover class="box-item" title="Title" content="Top Center prompts info" placement="top" v-if="!button">
    <template #reference>
      <div>top</div>
    </template>
  </el-popover>

  <el-tooltip class="box-item" effect="dark" content="Top Left prompts info" placement="top-start" v-if="!button">
    <div>top-start</div>
  </el-tooltip>
</template>
js 复制代码
<script setup>
import { ref } from "vue";
import { ElMessage, ElMessageBox, ElNotification } from "element-plus";

const model = ref(false);
const destroyDialogModelValue = ref(false);
const button = ref(false);

function fn0() {
  model.value = true;
  destroyDialogModelValue.value = true;
  ElMessage("This is a message.");
  ElMessageBox.alert("This is a message", "Title");
  ElNotification({
    title: "Title",
    message: "This is a reminder",
  });
}
function fn1() {
  console.log("closed");
  destroyDialogModelValue.value = false;
  button.value = true;
}
function reset() {
  model.value = false
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

如上代码,进入页面后,点击click,然后关闭所有的弹窗。然后再次点击reset按钮,然后再次点击click,关闭所有弹窗。如此可以多操作几次。

就发现了开头的组件,都存在内存泄漏问题。

未能解决

有问题,当然首先看看别人有没有出现过。各种搜索就不说了,大掘金也搜过,在Element-plus的github仓里的Issues中找过,发现的办法基本无用。

以下是自己思考的几条路子:

  1. 有泄漏的,都手搓一个?
  2. Eldialog全局只用一到两个?
  3. 将所有路由,都打成一个单页面(html)。
  4. 改源码....

结尾

还是在这里,求助大佬,看以上思路是否有错,然后跪求orz解决办法。

自己后续如果解决对应一些问题,会即时和大家分享。

相关推荐
Li_Ning2136 分钟前
【indexedDB】以web端多语言存储为例
前端
北极糊的狐42 分钟前
Vue 的响应式机制未生效——v-model 绑定的冰淇淋输入框值不更新
前端·javascript·vue.js
Dashingl1 小时前
uni-app AES 加密
android·javascript·uni-app
计算机毕设定制辅导-无忧学长1 小时前
从入门到精通:HTML 项目实战中的学习进度(一)
前端·学习·html
Microsoft Word1 小时前
HTML简单介绍
前端·html
DataGear1 小时前
DataGear 企业版 1.4.0 发布,数据可视化分析平台
javascript·信息可视化·数据分析·html·echarts·数据可视化
爱嘿嘿的小黑1 小时前
docker 常用命令
前端
dangfulin1 小时前
CSS——变换、过度与动画
前端·css
南屿欣风2 小时前
解决 Gin Web 应用中 Air 热部署无效的问题
前端·gin
猿大师办公助手2 小时前
Web网页内嵌福昕OFD版式办公套件实现在线预览编辑PDF、OFD文档
前端·pdf·word