Managing and Deploying Assets



Managing and Deploying Assets

0 0


asset-talk

Suncoast.js talk for March 2014

On Github tylerjohnst / asset-talk

Managing and Deploying Assets

Created by Tyler Johnston / @tylerjohnst

I'm Tyler Johnston

  • Co-owner of Ahoy Consulting
  • Ruby/Javascript/PHP/Whatever
  • Lover
  • You can find me on the Twitters: @tylerjohnst

Precompile? Why?

  • HTTP requests are expensive. Even worse with HTTPS.
  • Each browser has a fixed number of allowed requests at any given time. If there are 20 Javascript/CSS files that have to be fetched on each page load, only 5 concurrent requests will ever happen (Chrome).
  • Ability to use precompiled languages:
    • Coffeescript
    • Less, Sass, etc
    • Haml, Jade, etc

Where do I get started?

  • Command Line Based:
    • Grunt
    • Brocolli
    • Middleman
    • Ant / Make / Rake / Jake / Cake / Bash
  • GUI Based:
    • CodeKit
    • Coda

I prefer the command line and since this is a Javascript meetup I'll be walking through Grunt. (It's also one of the few I've used)

Bower is a dependancy management tool for CSS and Javascript libraries. Bower has a similar API to NPM. By default will clone the git repo to $PROJECT_ROOT/bower_components. This allows you to easially keep and manage third party libraries without keeping them in your code repo.

bower install bootstrap --save-dev

Automate your tasks away with grunt. Grunt is a task generator similar to Rake, Make, Cake, etc.

Grunt has plugins for every popular framework, tool, library, or latest test framework created a few minutes ago.

Getting started with grunt

# Gruntfile.coffee

module.exports = (grunt) ->
  grunt.initConfig
    pkg: grunt.file.readJSON("package.json")
          

Grunt Contrib

A very large collection of Grunt packages are available on Github and NPM as grunt-contrib-*

All grunt contrib use a similar API for implementing the tasks.

npm install grunt-contrib-mylib --save-dev

# Gruntfile.coffee
module.exports = (grunt) ->
  grunt.loadNpmTasks "grunt-contrib-mylib"

  grunt.initConfig
    mylib:
      taskname:
        options: []
          

Javascript Compilations

# Gruntfile.coffee

module.exports = (grunt) ->
  grunt.loadNpmTasks "grunt-contrib-concat"
  grunt.loadNpmTasks "grunt-contrib-uglify"

  grunt.initConfig
    pkg: grunt.file.readJSON("package.json")

    concat:
      javascripts:
        files:
          "tmp/application.concat.js": [
            "vendor/javascripts/jquery-1.10.2.min.js",
            "vendor/javascripts/lodash.compat.js",
            "vendor/javascripts/backbone.js",
            "vendor/javascripts/chosen.jquery.js",
            "vendor/javascripts/bootstrap.js",
            "app/application.js",
            "app/**/*.js"
          ]

    uglify:
      compile:
        files:
          "public/application.js" : ['tmp/application.concat.js']

  grunt.registerTask "compile:javascripts", ["concat:javascripts", "uglify:javascripts"]
          
To compile you just run: grunt compile:javascripts

CSS Compilation

# Gruntfile.coffee

module.exports = (grunt) ->
  grunt.loadNpmTasks "grunt-contrib-concat"

  grunt.initConfig
    pkg: grunt.file.readJSON("package.json")

    concat:
      stylesheets:
        files:
          "public/application.css": [
            "vendor/stylesheets/bootstrap.css",
            "app/stylesheets/**/*.css"
          ]

  grunt.registerTask("compile:stylesheets", ["concat:stylesheets"])
          
To compile you just run: grunt compile:javascripts

Grunt Watch

# Gruntfile.coffee

module.exports = (grunt) ->
  grunt.loadNpmTasks "grunt-contrib-watch"

  grunt.initConfig
    pkg: grunt.file.readJSON("package.json")

    watch:
      stylesheets:
        files: ["app/stylesheets/**/*", "app/vendor/stylesheets/**/*"]
        tasks: ["compile:stylesheets"]

      javascripts:
        files: ["app/javascripts/**/*.coffee", "app/javascripts/**/*.html", "app/vendor/javascripts"]
        tasks: ["compile:javascripts"]
          
Running grunt watch will set up the watcher process that will execute the tasks when a file changes on disk.

Whats next?

The the previous two slides should get you 99% of the way there. The only remaining thing to do is wire up the correct paths on your HTML files.
<link rel="stylesheet" type="text/css" href="/application.css">
<script src="/application.js"></script>
          

These examples are only the beginning of what you can do with grunt. Check out the gruntjs orgnanization on Github to see all the active projects they have going.

Caveats / Experience

I'm currently building an application using this as it's own asset pipeline. It works great for getting started but there are issues.

  • The compiler doesn't keep a cache or track if a specific file changes. It just recompiles the whole file again. This can become slow and cumbersome over time as your application becomes larger. Once you build in testing, you have to compile your whole app and then run tests.
  • Gruntfile's can become large and really annoying to change. It's not uncommon to end up with 200 line Gruntfiles.

Possible solutions

Brocolli - Built for speed. Uses a similar API to Grunt. Handles things like keeping caches of compiled JS so compile time is significantly faster. Written in Javascript and uses Node.js. Still very new and considered alpha quality software.

Possible solutions

Middleman - A Ruby alternative and my go-to for static site frontends. Essentially the Rails 3 asset pipeline for static sites. Works with any Ruby library built with Rails. Lots of out of the box support for this and can be very powerful with little to no configuration.

Thank You!

Questions? Comments?

@tylerjohnst