Saturday, February 1, 2014

SASS & BEM: how I improved compilation speed

Recently I finished a project which was using BEM methodology and SASS.

SASS is extremely useful, partially thanks to Compass framework, but I experienced a couple of issues when using it with BEM:

First, I was disappointed because older versions won't support this nice nested rules which Less CSS provides, to create .block__element class names:
.block {
    color:blue;
    
    &__element1 {
        color: red;
    }
    &__element2 {
        color: green;
    }
}

Though recent SASS v3.3 can do this:
.block
  color: red
  &__element
    color: red
  &__element
    color: green

Second, SASS, splitted to lots of small files, happened to be extremely slow.

Project layout for Django with BEM looked like this:
    base/
        static/
            base/
                base.sass
                button-mixins.sass
                base.js
    app1/
        static/
            app1/
                base.sass # includes base/base.sass
                base.js
                b-content/
                    b-content.sass # includes app1/base.sass 
                b-header/
                    b-header.sass
                    b-header__menu.sass
                    b-header.js
                b-footer/
                    b-footer.sass
                    b-footer__ads.sass
        templates/
            app1/
                b-content/
                    b-content.html
                b-header/
                    b-header.html
                    b-header__menu.html
                b-footer/
                    b-footer.html
                    b-footer__ads.html
    app2/
        static/
            app2/
               ...
        templates/
            app2/
               ...
    app3/
        ...

So it's a complex hierarchy of SASS files including each other.

And total CSS built time for was about 18 seconds (sic!) with SASS caching enabled. Imagine how much time you spend on refreshing pages when you make your web pages.

When I joined to work on this project, my first goal was is to try to increase performance yet retain current layout, if possible.

C++ implementation of SASS looked interesting, but lacks of Compass support.

Here are rules using which I improved SASS compilation speed drastically (tested on SASS v3.2.12):

1. Import only what you need, where you need it

Never use @import "compass", import exactly what you need in a .sass file where you need it, e.g. @import "compass/css3/border-radius" where you need to do +border-radius(). Otherwise it will import too much reducing performance. Global imports will result in unnecessary imports in all .sass file hierarchy branches. Applying this rule, I reduced SASS compilation time from 18 to 9 seconds. Also, looks like if you do @import 10 times in a file, it will be included & parsed 10 times.

Same to project's own .sass files. Reducing number of imports in project's own .sass files carefully won me also 1-3 seconds depending on case.

2. Compile only one SASS file for one page.

Initial setup used Django-Compressor, to collect lots of .sass files into one page. Though Compressor may compile everything into single CSS file, it does .sass files one by one.

The solution was is to make exactly one .sass file per page you use, and import all other .sass files into it. I.e. each Django template used one all.sass file.
    app1/
        static/
            app1/
                base.sass # includes base/base.sass
                base.js
                b-content/
                    b-content.sass
                    ...
                ...
                all.sass # includes base.sass, b-content.sass, etc

This won me about 5 seconds more, as starting ruby process for each .sass file took about 100ms.

So I've got about 1 second of compilation time and the issue was solved for me.

Still not sure about Sass v3.3, release notes saying it has some speed improvements.