Hướng dẫn nâng cấp Frontend Toolset với Grunt và LibSass

Grunt and LibSass

Learn how you can leverage Grunt and LibSass as groundwork to improve your Frontend Development process and deliver better optimized sites for clients.

>> Giới thiệu jQuery và Subtotaling trong Views Calc Tables - Drupal 7

>> Sử dụng Views Calc - để tính toán cho Fields trong Views module

At newmedia we've been utilizing Sass and Compass compiled with Ruby for CSS Preprocessing for quite some time now in our Drupal projects. While this toolset has served us well, the toolset for Frontend Developers has been growing and improving tremendously as time goes on. Thus, we want to leverage those tools not only to improve our day-to-day development tasks, but more importantly to better provide more optimized websites for our clients.

In this post we will walk through overhauling our Frontend tooling with Grunt and LibSass, what's involved, and what the benefits are for clients. For the purpose of this article, we're just going to focus on getting Grunt and LibSass up and running - from there the heavy lifting is done, and you're free to expand on your Grunt tasks as you see fit.

WHAT'S THE CLIENT BENEFIT?

Implementing these tools allows us to work faster and spend less time waiting for tools to run. The other key benefit is that, since Grunt runs arbitrary tasks, we can add more tools to ensure our end product is optimized - not just CSS, but also Javascript, images, and more, resulting in all-around faster-loading websites.

WHAT ARE THESE FRONTEND TOOLS & WHY ARE WE USING THEM?

Grunt is a task runner built in Javascript that leverages node.js. Currently, there are two popular task runner projects: Grunt and Gulp. While there are slight pros and cons between the two projects, we decided to implement Grunt for our Drupal projects, as our Wordpress team had already begun to use Grunt, and we wanted to converge some of our tooling and processes.

LibSass is an implementation of the Sass compiler written in C instead of Ruby. The primary benefit over the original Ruby compiler is a tremendous boost in speed resulting in much faster compile times.

Note on LibSass: Unfortunately LibSass does not yet have feature parity with Ruby Sass, but it's not too far off and continues to improve. This Sass Compatibility reference is handy for specific features that may be missing. Personally, I have yet to hit an issue with feature parity, but your mileage may vary.

GET THE PREREQUISITES IN PLACE

You will need node.js if you don't have it already. I suggest using the "n" version management tool to help manage your node install: https://github.com/tj/n

Once you have n installed, install the current stable version of node:


n stable

Next, you'll need to get a couple node modules installed globally: grunt-cli, the commandline interface for grunt, and bower, a dependency manager for web projects.

npm install -g grunt-cli
npm install -g bower

From here, many of the node modules we'll need can be managed at a project level.

SETTING UP GRUNT FOR YOUR PROJECT

Now that we have our prerequisites in place, we need to set up three files within our project:

  1. package.json to manage our project-level node dependencies
  2. Gruntfile.js to define our tasks
  3. bower.json will be used to define our sass library dependencies (in our case, singularity and breakpoint).

Special note on Drupal projects: Currently, placing node_modules within Drupal's directory can potentially cause it to crash, and otherwise could negatively impact performance. We structure our repository so that we have the Drupal directory contained in a folder called docroot, so we handle node at the root level of our project repo above the Drupal directory, rather than at the theme level. See these Drupal Core issues for more information:

package.json

Here's our example package.json:


{
  "name": "newmedia",
  "version": "0.0.1",
  "description": "newmedia grunt tools",
  "devDependencies": {
    "grunt": "^0.4.5",
    "grunt-contrib-watch": "^0.6.1",
    "grunt-sass": "^0.18.1",
    "load-grunt-tasks": "^3.1.0"
  }
}

The devDependencies section defines the node modules we're using:

  • grunt (Grunt itself is installed on a per-project basis, only the commandline interface grunt-cli is installed globally),
  • "grunt-contrib-watch", which provides a watch tool (think sass watch or compass watch),
  • grunt-sass, which will bring in and manage libsass for us, and
  • load-grunt-tasks, which allows us to automatically load our grunt dependencies in, rather than manually specifying each one in our Gruntfile.js

As times goes on, you'll likely want to add dependencies to your toolset. Adding them with "npm install [name] --save-dev" will download the module for you and automatically add it to your package.json.

bower.json

Using the Ruby Sass compiler, you use Bundler to manage your Sass library dependencies. Bower will take the place of that in our Grunt/LibSass workflow:


{
  "name": "newmedia",
  "version": "0.0.1",
  "description": "newmedia build dependencies",
  "authors": [
    "NEWMEDIA!"
  ],
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ],
  "devDependencies": {
    "breakpoint-sass": "~2.5.0",
    "singularity": "~1.6.2"
  }
}

Gruntfile.js

Now that we have dependency management out of the way, we can setup our tasks. Here's our example Gruntfile:


module.exports = function(grunt) {
  // Load tasks automatically with 'load-grunt-tasks' plugin.
  require('load-grunt-tasks')(grunt);

  // Define our theme directory
  var themeDir = 'path/to /theme';
  // Specify where to find dependencies we load in with Bower
  var sassLib = ['bower_components'];
  // Define the CSS files we want compiled from SCSS files
  var sassFiles = {};
  sassFiles[themeDir + '/css/style.css'] = themeDir + '/scss/style.scss';
  sassFiles[themeDir + '/css/print.css'] = themeDir + '/scss/print.scss';

  // Project configuration.
  grunt.initConfig({
    sass: {
      dev: {
        options: {
          sourceMap: true,
          outputStyle: 'expanded',
          includePaths: sassLib
        },
        files: sassFiles,
      },
      build: {
        options: {
          sourceMap: false,
          outputStyle: 'compressed',
          includePaths: sassLib
        },
        files: sassFiles,
      }
    },
    watch: {
      sass: {
        files: ['**/*.scss'],
        tasks: ['sass'],
      }
    },
  });

  // Default task(s).
  grunt.registerTask('default', ['sass:build']);

  grunt.registerTask('dev', ['sass:dev']);

  grunt.registerTask('build', ['sass:build']);

};

CHANGES TO YOUR SASS

Now that we have the tools in place, we will need to update how we import our sass libraries. Fortunately this is a simple change. Here are our existing imports:


@import "breakpoint";

@import "singularitygs";

These imports get updated to:


@import 'breakpoint-sass/stylesheets/breakpoint';
@import 'singularity/stylesheets/singularitygs';

The exact change to your Sass imports may be different depending on the library. To determine the path you need to use, add the library to bower.json, and look inside bower_components to locate the library and determine the path to the main scss file to import.

NOTE ON COMPASS

At the time of writing this post, Compass is still dependent on Ruby and can't be used with LibSass. Depending on your case, there are a couple options. If you are using Compass mixins and want to continue to use them, there is a compass-mixins library you can use with LibSass. If you primarily use Compass to handle vendor prefixes, you might want to look at adding Autoprefixer to your Grunt task instead.. If you're using other libraries that depend on Compass, it's likely that even using the compass-mixins library, you won't be able to use those libraries with LibSass.

READY TO GO!

At this point you should be in a good place to try running your grunt task. In our example, we have two defined tasks, build and dev, as well as our watch task. Try running "grunt dev" to do a one-time run. Once you are getting successful task runs, you are ready to start working on your project! You can now use "grunt watch" to detect changes and automatically run tasks.

You're also now at a good point to look at adding additional tasks to your workflow. Some suggestions would be to add tasks to handle optimizing your Javascript and image assets. You can find an expansive selection of tasks on the official Grunt Website’s plugin directory.