Web 前端開發日誌(三):HTML 節點的內存泄露問題

文章爲在下之前開發時的一些記錄與當時的思考, 學習之初的內容總會有所考慮不周, 若是出錯還請多多指教.javascript

TL;DR

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 消失了麼?函數

用 Memory 抓一下 Heap 看一下

咱們來用 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)
}
複製代碼

而後刷新頁面,再抓一個新的快照:

此次沒了!

問題在哪裏?

最先的代碼問題出如今了,deleteMyDivmyDiv 進行了引用,並且 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 帶來的內存泄漏問題.

相關文章
相關標籤/搜索