前言
通过下面这段代码,配合控制台可以直观看到谷歌官方承认的一个内存泄漏问题,https://issues.chromium.org/issues/41403456。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><div><button onclick="toggleInput(false)">创建一个2秒后自动销毁的文本框</button><button onclick="toggleInput(true)">创建一个2秒后自动销毁的聚焦文本框</button></div><script>function toggleInput(isFocus) {const ipt = document.createElement('input');ipt.value = "2秒后销毁"document.body.appendChild(ipt);isFocus && ipt.focus();setTimeout(() => {ipt.remove();}, 2000);}</script>
</body></html>
通过控制台的性能分析可以明显观察到:
当生成的input元素未聚焦
时,销毁后可以被垃圾回收
。
但当生成的input元素聚焦
了,则无法被垃圾回收
。
分析
官方觉得无可厚非,也没必要修改,事实上确实如此,并不会造成页面的卡死等问题。但秉着对技术刨根问底的心,分析内存泄漏
原因 并 尝试解决这个问题。
由于现在Web端多数都是单页面应用
,所以几乎不会出现「刷新」这个动作。那么如果有很多表单元素,会不会导致过多的垃圾无法被回收呢。
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><div><button onclick="createInputs()">创建五个自动销毁的文本框</button></div><script>function createInputs() {let times = 0let tId = setInterval(() => {if (times === 5) {clearInterval(tId);return}times++const ipt = document.createElement('input');ipt.value = "2秒后销毁"document.body.appendChild(ipt);ipt.focus();setTimeout(() => {ipt.remove();}, 200);}, 300);}</script>
</body></html>
通过观察可以发现多个聚焦的input,在手动清理后,只有一次的垃圾未被回收。
这是因为聚焦的input在销毁后,会自动转移到下一个input,所以手动清空垃圾后,仍保留一次垃圾,即最后一次。
解决
尝试通过blur()
在销毁元素前失焦,但未生效。
<div><button onclick="createInputs()">创建五个自动销毁的文本框</button>
</div>function createInputs() {let times = 0let tId = setInterval(() => {if (times === 5) {clearInterval(tId);return}times++const ipt = document.createElement('input');ipt.value = "2秒后销毁"document.body.appendChild(ipt);ipt.focus();setTimeout(() => {ipt.blur()ipt.remove();}, 200);}, 300);
}
但是如果聚焦到另一个input上,则可以正常被垃圾回收机制清理。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><div><button onclick="createInputs()">创建多个自动销毁的文本框</button><input type="text" id="testipt"></div><script>function createInputs() {let times = 0let tId = setInterval(() => {if (times === 5) {clearInterval(tId);return}times++const ipt = document.createElement('input');ipt.value = "2秒后销毁"document.body.appendChild(ipt);ipt.focus();setTimeout(() => {testipt.focus();ipt.remove();}, 200);}, 300);}</script>
</body></html>
我们只需要在页面中插入一个透明度0
或者极小
的input即可。