2012-06-18 64 views
12

我正在使用D3在骨幹視圖中進行圖形visuzalization。我允許用戶捏合縮放圖形,使用webkit轉換平滑過渡,並在發佈時重新繪製。爲了簡化代碼,我只是重新繪製新圖,而不是重新計算元素的新位置和大小(這是我的原始方法,但我的團隊請求重繪路線)。DOM節點清理在d3中如何工作?

[我通過twitter與Bostock交談。這實際上並不是首選的方式]

我注意到的事情是,對於每次重繪,我都會傾倒大量未清理的dom節點。

這與事件處理程序/閉包中的循環引用無關,因爲我禁用了除標籤之外的所有標籤(這些標籤沒有附加處理程序),並且發生相同的行爲。

我已經試圖積極地從圖中刪除元素,但dom節點似乎仍然泄漏。

下面是一些相關的代碼。 'render'被稱爲一組新的標籤。在完成變焦,「關閉」叫上老圖,一個新的與另一種觀點認爲實例創建,並調用「渲染」:

render: function() { 

     // create the svg offscreen/off dom 
     //document.createElementNS(d3.ns.prefix.svg, "svg") 
     var svg = this.svg = d3.select(this.el) 
      .append("svg:svg") 
      .attr('width', this.VIEW_WIDTH) 
      .attr('height', this.VIEW_HEIGHT) 

     this._drawTimeTicks.call(this, true); 
     return this; 
    }, 



_drawTimeTicks: function(includeLabels) { 
    var bounds = this.getDayBounds(); 
    var min = bounds.start; 
    var date = new Date(min); 
    var hour = 1000 * 60 * 60; 
    var hourDiff = 60 * this.SCALE; 
    var graphX = (date.getTime() - min)/1000/60; 
    var textMargin = 7; 
    var textVert = 11; 

    // Using for loop to draw multiple vertical lines 
    // and time labels. 

    var timeTicks = d3.select(this.el).select('svg'); 
    var width = timeTicks.attr('width'); 
    var height = timeTicks.attr('height'); 

    for (graphX; graphX < width; graphX += hourDiff) { 
     timeTicks.append("svg:line") 
      .attr("x1", graphX) 
      .attr("y1", 0) 
      .attr("x2", graphX) 
      .attr("y2", height) 
      .classed('timeTick'); 

     if (includeLabels) { 
      timeTicks.append("svg:text") 
       .classed("timeLabel", true) 
       .text(this.formatDate(date)) 
       .attr("x", graphX + textMargin) 
       .attr("y", textVert); 
     } 

     date.setTime(date.getTime() + hour); 
    } 



close: function() { 
     console.log("### closing the header"); 
     this.svg.selectAll('*').remove(); 
     this.svg.remove(); 
     this.svg = null; 
     this.el.innerHTML = ''; 

     this.unbind(); 
     this.remove(); 
    } 

正如你所看到的,我沒有做任何事情棘手與事件處理程序或關閉。通過幾次縮放交互,我可以泄漏幾十個不會被GC回收的dom節點。

這是內存泄漏還是d3在幕後做一些事情來優化未來的圖形構建/更新?有沒有更好的方法來銷燬我不知道的圖形?

任何想法?

回答

20

D3不保留任何隱藏的引用到您的節點,所以沒有「DOM節點清理」的內部概念。有簡單的選擇,這是DOM元素的數組,和DOM本身。如果您從DOM中移除了一個元素,並且沒有任何附加的引用,它應該被垃圾回收器回收。 (另外:目前還不清楚你所指的漏洞是DOM中剩餘的元素,還是垃圾收集器沒有回收孤立元素。過去,一些舊瀏覽器有垃圾收集DOM之間的循環引用元素和JavaScript關閉,但我不知道任何影響現代瀏覽器的問題。)

如果您正在更新DOM,執行此操作的最高性能方式是(通常)使用數據連接,因爲這允許您重用現有元素並僅修改需要更改的屬性。使用數據連接的關鍵功能對於object constancy也是一個好主意;如果在更新之前和之後都顯示相同的數據,那麼您可能不需要重新計算其所有屬性,並且瀏覽器的更新工作量較少。

在某些情況下,可以選擇更快的任意更新,例如更新封閉G元素的transform屬性,而不是更新後代元素的位置。作爲另一個例子,你可以簡單地通過改變viewBox屬性來在SVG元素中進行幾何縮放。但幾何縮放是一個非常有限的情況;一般而言,最有效的更新取決於正在發生的變化。使用數據連接,以便最大限度地減少您附加或刪除的元素數量,並最大限度地減少需要重新計算的屬性數量。

幾個其他的事情,我會指出...

  • 你可以使用一個數據連接,同時創建多個蜱,而不是使用一個for循環。 For循環幾乎從未用於D3,因爲數據連接可以創建多個元素(和分層結構)而不需要循環。更好的是,使用時間格式(d3.time.format)的軸組件(d3.svg.axis)和時標(d3.time.scale)。

  • D3的最新版本不需要「SVG:」命名空間,所以你可以附加「文」,「行」等

  • 我想不出任何情況下selectAll("*").remove()品牌感。 「*」選擇符匹配所有後代,所以這將從其父代中刪除每一個後代。您應該始終嘗試刪除最上面的元素(此處爲SVG容器),而不是刪除子元素的多餘部分。

+0

謝謝。我會研究不同的縮放選項,但之前我沒有使用它們的原因是我的圖形以矩形內的常量字體大小的文本爲特徵,並且我需要確定放大/縮小時所需的任何截斷。但是,再次感謝所有幫助:) – Gopherkhan

+0

是的,有幾個原因,爲什麼幾何變焦不足。有時你可以通過減小字體大小來增加縮放級別來修補這些問題,但通常以編程方式重新定位元素更爲簡單。 – mbostock

+0

我知道這是一個老問題,但在這裏查看一些關於DOM節點清理的特定於IE的注意事項:http://stackoverflow.com/questions/18575452/why-do-my-svg-nodes-leak-memory-in - 即 – explunit