Vor ein paar Tagen gab es einen albumup-Bugreport — die Galerieanssicht lud nicht vernünftig. Kurz ausprobiert, reproduziert, erstmal ein altes JavaScript-Bundle hochgeladen, was ich noch rumliegen hatte — klappt wieder. Ein Glück, wenn man vergessen hat die kompilierten Files ins Gitignore zu packen.

Heute versuchte ich zu herauszufinden, was da passiert. Der Chrome Task Manager ist dabei immer eine große Hilfe. Der Tab brauchte konstant 150% CPU und fraß immer mehr Speicher. Da war wohl was kaputt!

Normalerweise ist ja git auch sehr hilfreich bei solchen Sachen. Was habe ich wohl kurz vor dem letzten Live-Deployment kaputt gemacht? Ich ging alle Commits durch und fand… nichts. Am Galerie-Code hatte ich schon länger nicht mehr gearbeitet. Weil ich sonst absolut keine Idee mehr hatte, dachte ich, ich fange mal an ein paar Zahnräder aus dem System zu entfernen, um den Fehler einzugrenzen. Also einen Development-Build mit webpack durchgeführt, ohne Uglify und anderen Optimierungen. Tatsache, es funktioniert. Also erstmal einen grumpy Tweet abgesetzt und weiterprobiert.

In der Developer Console kam jedenfalls folgende Meldung:

Interessant, was? Hier muss man dazu sagen, dass die Meldung da schon länger ist und ich sie immer ignorierte, weil alles ja weiter funktionierte und ein kurzer Blick auf den Code den endlosen Loop nicht sofort erkennen ließ. Außerdem ist es ja nur eine Warning.

Tatsächlich ist es nun aber so, dass Vue im Development-Mode die Loops erkennt, abbricht und mit einer Warning versieht — damit hat man zwar ein paar rote Zeilen im Inspektor, dafür funktioniert die App aber an sich gut weiter. Im Production-Mode hingegen fehlt die Warning und alles läuft fröhlich im Kreis, bis der Browser stockt und der Macbook-Lüfter aufheult. Schade!

Irgendwie hatte es jedenfalls beim vorletzten Deployment ein Dev-Build auf den Server geschafft, was aber ganz glücklich war, denn der funktionierte wenigstens gut. Erst als ich vor ein paar Tagen aufräumte und einen vernünftigen kleinen Build auf den Server schob, ging alles kaputt.

Der Loop kam übrigens durch diese Zeilen Code:

displayFolders() {
    let folders =  this.$store.state.folders;
 
    const collator = new Intl.Collator(
        undefined, 
        {numeric: true, sensitivity: 'base'}
    );

    folders.sort(function(a, b) {
        return collator
            .compare(a.display_name, b.display_name);
    });

    return folders;
}

Die Funktion ist eine Computed Property, die jedes mal neu berechnet wird, wenn sich this.$store.state.folders ändert… und leider hab ich wohl vergessen, dass .sort ja den Array direkt sortiert und damit natürlich den State direkt ändert, was dazu führt, dass die Funktion erneut aufgerufen wird, was dazu führt, dass der State direkt geändert wird, was dazu führt, dass die Funktion aufgerufen wird…

Der Fix war auch erstmal relativ easy:

    let folders =  clone(this.$store.state.folders);

(Ja, das sollte eher in einen Getter, etc etc, mit Immutability wär dir das nicht passiert, etc etc)

Die Moral von der Geschichte: Danke Vue für hilfreiche Fehlermeldungen, die man aber auch nicht ignorieren sollte.