文章为在下之前开发时的一些记录与当时的思考, 学习之初的内容总会有所考虑不周, 若是出错还请多多指教.javascript
对 DOM
节点的引用会使得节点一直在内存中存储而不会进行释放.html
请各位考虑一下以下代码,这里有一个简易的 HTML:java
<div id="my-div">
<span>My Div</span>
<ul>
<li>Hero</li>
<li>Cows</li>
<li>Bugs</li>
</ul>
</div>
<button onclick="deleteMyDiv()">Delete My Div</button>
复制代码
还有一段比较丑的逻辑:typescript
<script> const myDiv = document.querySelector('#my-div') function deleteMyDiv () { myDiv.parentElement.removeChild(myDiv) } </script>
复制代码
请问,点击 "Delete My Div" 按钮时,节点 #my-div
消失了么?函数
咱们来用 Chrome 开发者工具中抓一下 Heap 观察一下,看看是否是能找到 Detached DOM
.工具
Detached DOM
表明一个 HTML 节点在 HTML 中被移除,但在内存中还保持引用的状态,意思就是,没删干净侧漏啦!性能
咱们来实际抓一下看看,咱们抓两次快照,第一次是在点击删除按钮前的快照,以下图:学习
咱们在搜索框中输入 detached
,看起来并无 Detached 节点.ui
咱们点击页面中的 "Delete My Div" 后来抓第二次:spa
嚯,这就有了!选中它看看:
看看这个 div#my-div
,I'm angry!
咱们来稍微调整一下逻辑:
function getMyDiv () {
return document.querySelector('#my-div')
}
function deleteMyDiv () {
const myDiv = getMyDiv()
myDiv.parentElement.removeChild(myDiv)
}
复制代码
而后刷新页面,再抓一个新的快照:
此次没了!
最先的代码问题出如今了,deleteMyDiv
对 myDiv
进行了引用,并且 deleteMyDiv
没有进行回收,因此致使 myDiv
的一直存在.
咱们再仔细看如下刚才截取的快照:
就算是将 myDiv
从 HTML 中删除,内存中也会一直保存其数据不会释放.
修改以后的代码没有任何地方保持了对 myDiv
的引用,因此在删除操做执行完毕后会马上释放 div#my-div
节点.
实际上这样的状况在业务逻辑中很是容易出现,好比建立一个比较复杂的节点,这个节点中包含了不少细碎的节点,甚至这些节点是从别的业务服务中传入进来的,这时就很难保证这些细碎的节点没有被其建立函数或外部代码引用,因此就算在 HTML 中删除,也颇有可能形成泄漏.
若是删除一个节点,这个节点没有被直接引用,但里面的子节点被引用,会怎么样?
<div id="my-div">
<div id="inner-div">Inner DIV</div>
</div>
<button onclick="deleteMyDiv()">Delete My Div</button>
复制代码
// 加入 innerDiv.
const innerDiv = document.querySelector('#inner-div')
function getInnerDiv () {
return innerDiv
}
// 下边仍是以前的代码.
function getMyDiv () {
return document.querySelector('#my-div')
}
function deleteMyDiv () {
const myDiv = getMyDiv()
myDiv.parentElement.removeChild(myDiv)
}
复制代码
截取快照:
能够看到两个都已经变成 Detached Dom
,批判一番!🐸🔫
实际上在下对于这种状况在下目前并无很是好的预防实践,只能说你们对敏感的 DOM 操做逻辑进行谨慎地编写,并在出现问题的时候借助工具快速排查,若是有更好的实践还请你们多分享交流.
对于性能要求比较高的场景,使用原生代码建立节点仍是请当心谨慎,避免 Detached Dom
带来的内存泄漏问题.