前言
通过下面这段代码,配合控制台可以直观看到谷歌官方承认的一个内存泄漏问题,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 = 0
let 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 = 0
let 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 = 0
let 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即可。