Vue 中导致 detached 元素产生的常见行为
在 Vue 应用中,detached 元素指的是已经从 DOM 树中移除但仍然被 JavaScript 引用着的 DOM 节点。这些元素会导致内存泄漏,因为它们占用的内存无法被垃圾回收机制回收。以下是 Vue 中常见会导致 detached 元素产生的行为:
1. 未正确清理的全局事件监听器
javascript
mounted() {
window.addEventListener('resize', this.handleResize)
},
beforeDestroy() {
// 如果忘记移除监听器,相关元素可能保持引用
// window.removeEventListener('resize', this.handleResize)
}
2. 第三方库未正确销毁
javascript
mounted() {
this.chart = new Chart(this.$refs.canvas, {...})
},
beforeDestroy() {
// 如果忘记调用销毁方法
// this.chart.destroy()
}
3. 未清理的定时器
javascript
data() {
return {
timer: null
}
},
mounted() {
this.timer = setInterval(() => {
// 一些操作
}, 1000)
},
beforeDestroy() {
// 如果忘记清除定时器
// clearInterval(this.timer)
}
4. 未清理的 Vue 自定义事件
javascript
mounted() {
this.$on('custom-event', this.handleEvent)
},
beforeDestroy() {
// 如果忘记移除事件监听
// this.$off('custom-event', this.handleEvent)
}
5. 在组件销毁后仍然持有 DOM 引用
javascript
data() {
return {
elements: []
}
},
methods: {
cacheElement() {
this.elements.push(document.getElementById('some-element'))
}
}
// 如果 elements 数组不清空,引用的 DOM 元素会保持
// 这时候包含some-element元素的组件被销毁,DOM的引用依然存在
6. 使用 v-if 切换组件时的引用保留
javascript
// 父组件
<template>
<child-component v-if="showChild" ref="child"/>
</template>
// 如果在其他地方保持了对 this.$refs.child 的引用
// 即使child-component组件被销毁,引用仍然存在
mounted() {
this.detachedElement = this.$refs.child;
}
如何检测
方式一
- 使用 Chrome DevTools 的 Memory 工具
- 先手动触发垃圾回收
- 拍摄堆快照(Heap snapshot)
- 搜索 "detached" 来查找分离的 DOM 树
方式二
- 使用 Chrome DevTools 的 Memory 工具
- 先手动触发垃圾回收
- 拍摄分离的元素(Detached elements)
拍摄分离的元素可能导致原本被回收的 elements 没有被回收,所以多次拍摄会产生影响,关闭 DevTools 或刷新页面后引用会释放。拍摄堆快照无此问题。