SASS Performance

SASS Performance

Von am 24.11.2014

Bis vor einer Woche hätte ich nie geglaubt, dass man mit CSS die Performance einer Website so in die Knie zwingen kann, dass sie unbenutzbar wird. Doch das geht leider sogar sehr gut. Die Ursache war eine Kombination aus zwei SASS Features. Dies Ursache und wie man so etwas vermeiden kann wird in diesem Blog-Beitrag erläutert.


Bei zoomsquare arbeiten wir gerade an einer neuen Version unserer Immobilien-Suchmaschine für Desktop-User. Nachdem wir im Sommer unsere erste App für iOS und Android starten konnten wollen wir nun auch den Desktop Usern ein starkes Tool zur Wohnungssuche in die Hand geben.

Die Desktop Version basiert auf meteor, einer Open Source Plattform für Web Applikationen in JavaScript. Das Interface selbst basiert auf einem eigenen Framework, das mit SASS beschrieben wird. So weit zu den Basistechnologien. Das SASS Framework besteht aus grundlegenden Styles für Typografie, Images, Forms, enthält Module wie ein Flyout Menü, Modale, Tabs und viele mehr. Was auch im Framework definiert wird ist das Grid, das zur Ausrichtung von Element verwendet wird. Hier habe ich mich vor ein paar Monaten für das Grid System von Harry Roberts, csswizardry-grids, entschieden.

csswizardry-grids

Auf github kann man nachlesen wie genau das Grid-System arbeitet. Prinzipiell besteht es aus einem einzigen SASS File. Am Anfang der Datei, können einige Variablen gesetzt werden, um das Grid an die eigenen Bedürfnisse anzupassen. Eine dieser Variablen betrifft das „silent-classes“-Feature.

Grundsätzlich weist man einem Element mittels Klassen zu wie es sich verhalten soll.

<div class="element grid__item lap--one-half desk--two-thirds">
 ...
</div></pre>
<pre>

grid__item = sei ein Item in einem Grid
lap—one-half = nimm im Breakpoint „lap“ die Hälfte der Breite ein
desk—two-thirds = nimm im Breakpoint „desk“ zwei Drittel der Breite ein

Wer es vermeiden möchte zu viele Klassen im HTML zu haben kann auf das „silent-classes“-Feature umstellen. Damit werden die Grid-Anweisungen auf das CSS ausgelagert.

.element
 {
      @extend %grid__item;
      @extend %lap—one-half;
      @extend %desk—two-thirds;
 }

SASS Features

So weit so gut. Das bringt uns zu den zwei Features, die jetzt ernsthafte Performance Probleme ausgelöst hat.

Nesting
SASS erlaubt es Selektoren zu verschachteln. Hier ein Beispiel:

.ul
 {
      margin: 0;
      padding: 0;
 
      .li
      {
           display: inline-block;
      }
 }

Extend/Inheritance

Ein weiteres tolles Features von Sass ist @extend, mit dem es möglich ist einen bereits geschriebenen Selektor in einem anderen zu verwenden. Hier ein Beispiel:

.box
 {
      margin: 12px;
      padding: 10px;
 }
 
 .box—success
 {
      @extend .box;
      background-color: red;
 }

Das Problem

Nach ein paar Wochen Entwicklungszeit war Chrome und vor allem die Entwicklertools völlig überfordert mit der Web-App. Hat man ein Element untersucht hat es 2-3 Sekunden gedauert, bis die Styles sichtbar waren. Resizen des Fensters dauerte wieder 5-8 Sekunden, bis die Seite neu gerendert war. Christoph äußerste dann den Verdacht, dass es an dem Grid System liegt, weil die Selektoren so merkwürdig lang waren. Das wollte ich zunächst nicht glauben, hab dann aber doch recherchiert. Und er hatte recht.

Folgender SASS Code sieht auf den ersten Blick völlig in Ordnung aus:

.container
 {
          .l-box
          {
             @extend %grid;
             @extend %grid--full;
 
             .box
             {
                @extend %grid__item;
                @extend %one-whole;
                @extend %huge--one-half;
             }
          }
 }

Sieht man sich allerdings das kompilierte CSS an, sollte man schon stutzig werden.

.container .l-box {
     list-style: none;
     margin: 0;
     padding: 0;
     margin-left: -24px;
     letter-spacing: -0.25em;
 }
 
 .container .l-box .box {
     display: inline-block;
     vertical-align: top;
     width: 100%;
     -moz-box-sizing: border-box;
     box-sizing: border-box;
     letter-spacing: normal;
     word-spacing: normal;
 }
 
 .container .l-box {
     margin-left: 0;
 }
 
 .container .l-box > .container .l-box .box, .container .l-box .container .l-box > .box {
     padding-left: 0;
 }
 
 
 .container .l-box .box {
     width: 100%;
 }
 
 @media only screen and (min-width: 1060px) {
     .container .l-box .box {
         width: 50%;
     }
 }

Die Menge an Selektoren wird natürlich dann noch mehr, je mehr Elemente am Grid ausgerichtet werden und je tiefer diese Selektoren genestet sind.

Baut man das SASS jetzt unverschachtelt auf erreicht man folgendes Ergebnis:

.l-box {
     list-style: none;
     margin: 0;
     padding: 0;
     margin-left: -24px;
     letter-spacing: -0.25em;
 }
 
 .box {
     display: inline-block;
     padding-left: 24px;
     vertical-align: top;
     width: 100%;
     -moz-box-sizing: border-box;
     box-sizing: border-box;
     letter-spacing: normal;
     word-spacing: normal;
 }
 
 .l-box {
     margin-left: 0;
 }
 
 .l-box > .box {
     padding-left: 0;
 }
 
 .box {
     width: 100%;
 }
 
 @media only screen and (min-width: 1060px) {
     .box {
         width: 50%;
     }
 }

Der Unterschied beim Drittletzten Selektor, der padding-left auf 0 setzt ist sehr deutlich. Von 10 Ebenen wurde jetzt auf 2 reduziert. Als ich die ganzen Projektfiles auf solche Fälle durchsucht und alle Verschachtelungen entfernt hab war die Web-App auf ein Mal wieder Blitzschnell und Chrome konnte wieder aufatmen.

Sowohl nesting als auch extend machen Sinn und sind sehr hilfreich. Mein Tipp ist allerdings sie nicht willkürlich zu verwenden, sondern nur dann, wenn es auch wirklich sinnvoll ist. Denn oft gibt es auch andere Lösungen.

The comments are closed.