聚焦的游离节点导致的内存泄露:
`最近做项目开发的时候发现了一个内存泄露问题,我把这个问题单独抽出来写一个html文件进行分析
问题描述
提示:这里描述项目中遇到的问题:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<p>
<button onclick="toogleInput(false)">创建一个2秒后销毁的文本框</button>
<button onclick="toogleInput(true)">创建一个2秒后销毁的聚焦文本框</button>
<input type="text" id="res">
</p>
<script>
function toogleInput(isFocus) {
const inp = document.createElement("input");
inp.value = "2秒后销毁";
document.body.appendChild(inp);
isFocus && inp.focus();
setTimeout(() => {
inp.remove();
}, 2000);
}
</script>
</body>
</html>
我们先进行垃圾清理,确保内存是干净的,之后用谷歌浏览器进行录制,点击第一个按钮,2秒后文本框消失后我们进行垃圾清理并停止录制;
我们直接看性能分析结果
我们发现经过垃圾回收内存是可以被回收掉的,但是如果文本框是聚焦的,那么就会出问题,我们重新录制,这次录制我们点击第二个按钮,当2秒后聚焦文本框消失的时候,进行垃圾清除后停止录制。
我们会发现聚焦文本框的内存并没有被回收,这是怎么回事呢?
原因分析:
之后我开始找原因,先排除是不是偶然事件,所以我又写了一段代码,创建多个聚焦文本框
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<p>
<button onclick="toogleInput(false)">创建一个2秒后销毁的文本框</button>
<button onclick="toogleInput(true)">创建一个2秒后销毁的聚焦文本框</button>
<button onclick="createInputs()">创建10次聚焦的文本框</button>
</p>
<script>
function toogleInput(isFocus) {
const inp = document.createElement("input");
inp.value = "2秒后销毁";
document.body.appendChild(inp);
isFocus && inp.focus();
setTimeout(() => {
inp.remove();
}, 2000);
}
</script>
<script>
function createInputs() {
let times = 0;
let tId = setInterval(() => {
if (times === 10) {
clearInterval(tId);
return;
}
times++;
const inp = document.createElement("input");
document.body.appendChild(inp);
inp.focus();
setTimeout(() => {
inp.remove();
}, 200);
}, 300);
}
</script>
</body>
</html>
然后进行录制分析,这次结果又和自己想的不一样,他确实是回收了,但是没完全回收,这时候我们就猜想是不是最后一个聚焦文本框就不会回收呢?
解决方案:
在页面上增加一个用于切换焦点的文本框
<p>
<button onclick="toogleInput(false)">创建一个2秒后销毁的文本框</button>
<button onclick="toogleInput(true)">创建一个2秒后销毁的聚焦文本框</button>
<button onclick="createInputs()">创建10次聚焦的文本框</button>
<input type="text" id="res">
</p>
在remove之前切换焦点
<script>
function toogleInput(isFocus) {
const inp = document.createElement("input");
inp.value = "2秒后销毁";
document.body.appendChild(inp);
isFocus && inp.focus();
setTimeout(() => {
res.focus() // 加上这句代码
inp.remove();
}, 2000);
}
</script>
之后我们看效果,发现这种做法是可以的
我们测试多个聚焦文本框的,发现只要切换最后一个聚焦文本框的焦点,内存就可以被回收
<script>
function createInputs() {
let times = 0;
let tId = setInterval(() => {
if (times === 10) {
clearInterval(tId);
return;
}
times++;
const inp = document.createElement("input");
document.body.appendChild(inp);
inp.focus();
setTimeout(() => {
res.focus() // 加上这句代码
inp.remove();
}, 200);
}, 300);
}
</script>
总结
聚焦的游离节点导致的内存泄露:由于谷歌浏览器没法回收最后一个聚焦的文本框,并且如果项目中一个聚焦的富文本框有较多的元素和数据,频繁切换路由时会导致内存不断增加;基于此我们可以采用切换切换焦点的方式解决该内存泄露问题。