2012-06-18 3 views
12

Ich arbeite an einer Graph-Visualisierung mit D3 in einer Backbone-Ansicht. Ich erlaube dem Benutzer, den Graphen zu zoomen, mit Hilfe von Webkit-Transformationen fließend zu wechseln und bei der Veröffentlichung neu zu zeichnen. Um den Code einfach zu halten, zeichne ich die Grafik nur auf der neuen Skala neu, anstatt die neuen Positionen und Größen für die Elemente neu zu berechnen (dies war mein ursprünglicher Ansatz, aber mein Team hat die Neuzeichnungsroute angefordert).Wie funktioniert die DOM-Knotenbereinigung in d3?

[Ich sprach mit Bostock über Twitter. Dies ist eigentlich nicht die bevorzugte Art, Dinge zu tun]

Die Sache, die ich bemerke, ist, dass ich für jedes Neuzeichnen Tonnen von dom Knoten, die nicht aufgeräumt sind.

Dies bezieht sich nicht auf zirkuläre Verweise in Ereignishandlern/-schließungen, da ich alle außer meinen Labels deaktiviert habe (diese haben keine Handler angehängt), und das gleiche Verhalten tritt auf.

Ich habe versucht, aggressiv die Elemente aus dem Diagramm zu entfernen, aber die Dom-Knoten scheinen immer noch zu lecken.

Hier ist ein paar relevante Code. 'render' wird für einen neuen Etikettensatz aufgerufen. Beim Zoomen Beenden ‚schließen‘ wird auf dem alten Graphen genannt, und ein neues Geschäft mit einer anderen Ansicht Instanziierung erstellt und rufen zu ‚machen‘:

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(); 
    } 

Wie Sie sehen können, bin ich nichts zu tun heikel mit Event-Handlern oder Schließungen. Mit ein paar Zoom-Interaktionen kann ich Dutzende von dom-Knoten durchsickern lassen, die niemals von GC zurückgewonnen werden.

Ist dies ein Speicherleck, oder macht d3 etwas hinter den Kulissen, um zukünftige Grafikkonstruktionen/Aktualisierungen zu optimieren? Gibt es eine bessere Möglichkeit, eine Grafik zu zerstören, die mir nicht bekannt ist?

Irgendwelche Ideen?

Antwort

20

D3 speichert keine versteckten Verweise auf Ihre Knoten, daher gibt es kein internes Konzept der "DOM node cleanup". Es gibt einfach Auswahlen, die Arrays von DOM-Elementen sind, und das DOM selbst. Wenn Sie ein Element aus dem DOM entfernen und keine zusätzlichen Verweise darauf beibehalten, sollte es vom Garbage Collector zurückgenommen werden.

(abgesehen: es ist nicht klar, ob das Leck, Sie beziehen sich Elemente im DOM oder verwaiste Elemente verbleibenden nicht vom Garbage Collector freigegeben werden in der Vergangenheit einige ältere Browser hatte Bugs Müllsammel zirkuläre Verweise zwischen DOM. Elemente und JavaScript-Schließungen, aber mir sind solche Probleme nicht bekannt, die moderne Browser betreffen.)

Wenn Sie das DOM aktualisieren, ist die performanteste Art dies zu tun (im Allgemeinen) eine Datenverbindung, weil Dadurch können Sie vorhandene Elemente wiederverwenden und nur die Attribute ändern, die geändert werden müssen. Die Verwendung einer Schlüsselfunktion für die Datenverbindung ist auch eine gute Idee für object constancy; Wenn dieselben Daten sowohl vor als auch nach der Aktualisierung angezeigt werden, müssen Sie möglicherweise nicht alle Attribute neu berechnen, und der Browser benötigt weniger Aktualisierungen.

In bestimmten Fällen gibt es Alternativen zu willkürlichen Aktualisierungen, die schneller sind, z. B. das Transformationsattribut eines umschließenden G-Elements aktualisieren, statt die Positionen der untergeordneten Elemente zu aktualisieren. Als weiteres Beispiel können Sie geometrisches Zoomen innerhalb eines SVG-Elements durchführen, indem Sie einfach das viewBox-Attribut ändern. Aber geometrisches Zoomen ist ein sehr begrenzter Fall; Im Allgemeinen hängt das effizienteste Update davon ab, was genau sich ändert. Verwenden Sie einen Datenjoin, um die Anzahl der anzuhängenden oder zu entfernenden Elemente zu minimieren und die Anzahl der Attribute zu minimieren, die neu berechnet werden müssen.

Ein paar andere Dinge, die ich darauf hinweisen werde ...

  • Sie könnten eine Datenverknüpfung verwenden mehrere Zecken zur gleichen Zeit zu schaffen, anstatt ein for-Schleife. For-Schleifen werden fast nie mit D3 verwendet, da der Daten-Join mehrere Elemente (und hierarchische Strukturen) ohne Schleifen erzeugen kann. Noch besser, verwenden Sie die Achsenkomponente (d3.svg.axis) und eine Zeitskala (d3.time.scale) mit einem Zeitformat (d3.time.format).

  • Neuere Versionen von D3 erfordern nicht die „svg:“ Namespace, so können Sie „Text“, „Linie“ anhängen usw.

  • ich nicht von jeder Situation denken kann, wo selectAll("*").remove() Marken Sinn. Der "*" - Selektor stimmt mit allen Nachkommen überein, so dass jeder einzelne Nachfolger von seinem Elternknoten entfernt wird. Sie sollten immer versuchen, das oberste Element - den SVG-Container hier - zu entfernen, anstatt redundante Entfernungen von untergeordneten Elementen zu entfernen.

+0

Danke. Ich schaue mir die verschiedenen Zoom-Optionen an, aber der Grund, warum ich sie vorher nicht verwendet habe, war, dass mein Graph Text mit konstanter Schriftgröße innerhalb von Rechtecken enthält, und ich muss jede notwendige Beschneidung beim Vergrößern/Verkleinern bestimmen. Aber nochmal, danke für die Hilfe :) – Gopherkhan

+0

Ja, es gibt viele Gründe, warum geometrisches Zoomen nicht ausreicht. Manchmal können Sie diese Probleme beheben, indem Sie die Schriftgröße verringern und gleichzeitig die Zoomstufe erhöhen. Oft ist es jedoch einfacher, Elemente programmatisch neu zu positionieren. – mbostock

+0

Ich weiß, dass dies eine alte Frage ist, aber sieh dir hier einige IE-spezifische Anmerkungen zur DOM Knotenbereinigung an: http://StackOverflow.com/questions/18575452/why-do-my-svg-nodes-leak-memory-in -ie – explunit

Verwandte Themen