From Sass(+Compass) to libSass(+compass-mixins)

最近又要對抄筆記做不小個改動,於是打算來解決一個困擾我已久的問題: Sass/Compass 有夠慢!

http://lmgtfy.com/?q=sass+slow

舊做法:Compass + concat

其實很早以前我就已經嘗試處理過這個問題,原本所有需要 compile 的 @import 都會回歸到同一個 .scss 檔案,但由於每次在 watch 的狀態下按存檔我都要等個 10 秒左右,最後我就受不了了,於是出現了如下的結構:

sass_tree_01.png sass_tree_02.png

這樣每次會生出 一堆 css(沒有前置底線的都會生成 .css 檔案),然後再透 Grunt 在這些 .css 檔生出來之後把它們 concat 起來,如此在只更改單一檔案的時候(e.g. sass/cards/new-features.scss)就不會需要同時 compile 其它的一大包東西(是說我本來以為 Sass 本身的 cache 機制進場可以解決這個問題,但上網查了一下跟親自試了一下之後覺得似乎沒有什麼幫助…)

此時的 Gruntfile.js (我是用 grunt-contrib-compass) 就生出了這兩段新的 configuration:

// in grunt.initConfig
concat: {
  css: {
    options: {
      separator: ''
    },
    src: [
      '<%= config.concatDir %>/css/*.css',
      '<%= config.concatDir %>/css/cards/*.css'
    ],
    dest: '<%= config.distDir %>/content/css/app.css'
  }
},
watch: {
  css: {
    files: ['<%= config.concatDir %>/css/{,**/}*.css'],
    tasks: ['concat:css']
  }
}

然而在碰到修改的是共用的 _ xxx.scss 或著是需要整包做一次重新 compile 就會要等有點久…

dev_css.gif

新做法:libSass + compass-mixins

  • libSass

    Sass engine 的 C/C++ port 版,運作速度非常非常之快。並且有各個 language 的 wrapper,都可以在頁面上找到。

  • compass-mixins

    脫離了 Ruby Compass 的魔掌之後仍然有 Compass 大部分的 mixin 可以用。

  • grunt-sass

    Grunt + node-sass

安裝必須品

npm install --save-dev compass-mixins
npm install --save-dev grunt-sass

修改 Gruntfile.js

// in grunt.initConfig
sass: {
  options: {
    includePaths: [
      // 把 compass-mixins 加到 sass 的 include path
      'node_modules/compass-mixins/lib'
    ]
  },
  dist: {
    options: {
      sourceComments: true
    },
    // 用 grunt 展開檔案丟給 node-sass
    // ref: http://gruntjs.com/configuring-tasks#globbing-patterns
    files: [{
      expand: true,
      cwd: '<%= config.appDir %>/sass',
      src: '{,**/}*.scss',
      dest: '<%= config.concatDir %>/css',
      ext: '.css'
    }]
  }
}

值得注意的是如果是從 Compass 轉移過來的話,原本 Compass 是自動 compile 整個設定的 Sass 資料夾內非 _ 開頭的 Sass 檔案,而 node-sass 的 compile 是以單一檔案為基礎的,因此在這邊我們需要透過 Grunt 的 globbing patterns 把原本 Compass 設定的 Sass 資料夾內的檔案展開餵給 grunt-sass。

如果你沒有這個需求,可以直接參考 grunt-sass 的 README 有最基本的一對一設定方法:

sass: {
  options: {
    sourceMap: true
  },
  dist: {
    files: {
        'main.css': 'main.scss'
    }
  }
}

馬上來試一下速度怎麼樣!

dev_css_after.gif

後記

其實當時要改成 Compass + concat 前我就已經有稍微調查過 node-sass,不過那時候恰恰是 Sass 迎來升上 3.3 並且 Compass 迎向 1.0 的可怕時刻,然而偏偏我就是需要 Sass 3.3 才有的功能,而 libSass 的進展畢竟還是沒有直接跟上 Sass,最後我只好作罷。

Switching from Ruby Sass to LibSass http://www.sitepoint.com/switching-ruby-sass-libsass/

時過境遷,後來 Ruby Sass 停下來等 libSass 追上後再齊頭並進,才有了今天這麼快速又不落人後的 libSass!

Sass Compatibility http://sass-compatibility.github.io/

果然在這個變化快速的產業,偶爾就是需要讓子彈飛一陣子。

giphy.gif