Successfully reported this slideshow.

Sprockets

15,458 views

Published on

Sprockets is an easy solution to managing large JavaScript codebases by letting you structure it, bundle it with related assets, and consolidate it as one single file, with pre-baked command-line tooling, CGI front and Rails plugin. It's a framework-agnostic open-source solution that makes for great serving performance while helping you structure and manage your codebase better.

Published in: Technology

Sprockets

  1. Sprockets You’ve got tons of JavaScript. Time to do something about it. Christophe Porteneuve JSConf.eu 2009
  2. Hi, I’m Christophe • I’m 32 and I live in Paris, France • I use Rails all day at work • I’m a Prototype Core member • I wrote Prototype and script.aculo.us (“The Bungee Book”) • I’m glad to be here!
  3. The problem
  4. The problem • JavaScript is the next big language…
  5. The problem • JavaScript is the next big language… • It’s already getting big, codewise!
  6. The problem • JavaScript is the next big language… • It’s already getting big, codewise! • Many apps have countless JS files
  7. The problem • JavaScript is the next big language… • It’s already getting big, codewise! • Many apps have countless JS files • Loading → lots of requests → latence
  8. The problem • JavaScript is the next big language… • It’s already getting big, codewise! • Many apps have countless JS files • Loading → lots of requests → latence • Or you could maintain one big file…
  9. The problem • JavaScript is the next big language… • It’s already getting big, codewise! • Many apps have countless JS files • Loading → lots of requests → latence • Or you could maintain one big file… • …which sucks
  10. The solution
  11. The solution • You want to split your JS code in neat files and folders!
  12. The solution • You want to split your JS code in neat files and folders! • You want some structure, dammit!
  13. The solution • You want to split your JS code in neat files and folders! • You want some structure, dammit! • But you want to serve just one file!
  14. The solution • You want to split your JS code in neat files and folders! • You want some structure, dammit! • But you want to serve just one file! • Enter Sprockets
  15. So many cool things…
  16. So many cool things… • Use require directives and load paths to structure your source
  17. So many cool things… • Use require directives and load paths to structure your source • Preprocess constants!
  18. So many cool things… • Use require directives and load paths to structure your source • Preprocess constants! • Create “JS plugins,” complete with related CSS/image assets, bundle them and distribute them easily
  19. Getting organized
  20. Getting organized … //= require "lang" //= require "ajax" //= require "dom"
  21. Getting organized //= require "dom/dom" //= require "dom/selector" //= require "dom/form" //= require "dom/event" Element.addMethods(); … //= require "lang" //= require "ajax" //= require "dom"
  22. Getting organized //= require "ajax/ajax" //= require "ajax/responders" //= require "ajax/base" //= require "ajax/request" //= require "ajax/response" //= require "ajax/updater" //= require "ajax/periodical_updater" //= require "dom/dom" //= require "dom/selector" //= require "dom/form" //= require "dom/event" Element.addMethods(); … //= require "lang" //= require "ajax" //= require "dom"
  23. The load path Sprockets has a load path, much like C’s or Ruby’s
  24. The load path Sprockets has a load path, much like C’s or Ruby’s Using the load path: //= require <coolobj> //= require <coollib/coolobj>
  25. The load path Sprockets has a load path, much like C’s or Ruby’s Using the load path: //= require <coolobj> //= require <coollib/coolobj> Using paths relative to the current file: //= require "coolobj" //= require "subdir/coolobj"
  26. Constants FTW! Put a constants.yml file somewhere in your load path • It’s a YAML file • Heard of YAML, right? • Super simple yet powerful
  27. Constants for you
  28. Constants for you
  29. Constants for you PROTOTYPE_VERSION: 1.6.1_rc3
  30. Constants for you PROTOTYPE_VERSION: 1.6.1_rc3 var Prototype = { Version: '<%= PROTOTYPE_VERSION %>', Browser: (function(){ …
  31. Bundling assets //= require "color" //= provide "../assets" Because you require <color_picker>, Sprockets can copy the assets to your asset root (somewhere inside your document root)
  32. I can haz? • It’s a Ruby gem • You don’t need to know Ruby though • You do need Ruby and Rubygems installed
  33. I can haz? • It’s a Ruby gem • You don’t need to know Ruby though • You do need Ruby and Rubygems installed $ sudo gem install --remote sprockets
  34. As a command-line tool… $ sprocketize Usage: sprocketize [options] filename [filename ...] -C, --directory=DIRECTORY Change to DIRECTORY before doing anything -I, --include-dir=DIRECTORY Adds the directory to the Sprockets load path -a, --asset-root=DIRECTORY Copy provided assets into DIRECTORY … -h, --help Shows this help message -v, --version Shows version
  35. As a command-line tool… $ sprocketize Usage: sprocketize [options] filename [filename ...] -C, --directory=DIRECTORY Change to DIRECTORY before doing anything -I, --include-dir=DIRECTORY Adds the directory to the Sprockets load path -a, --asset-root=DIRECTORY Copy provided assets into DIRECTORY … -h, --help Shows this help message -v, --version Shows version $ sprocketize -I src -a dist src/application.js > dist/javascripts/application.js
  36. Within Ruby code…
  37. Within Ruby code… require 'rubygems' require 'sprockets' secretary = Sprockets::Secretary.new( :asset_root => "public", :load_path => ["vendor/sprockets/*/src", "vendor/plugins/*/javascripts"], :source_files => ["app/javascripts/application.js", "app/javascripts/**/*.js"] ) concatenation = secretary.concatenation concatenation.save_to("public/sprockets.js") secretary.install_assets
  38. As a CGI script… Say you have this tree
  39. As a CGI script… Say you have this tree :load_path: - javascripts - vendor/sprockets/*/src :source_files: - javascripts/mysite.js - javascripts/*.js :output_file: public/sprockets.js So you write this sprockets.yml
  40. As a CGI script… <VirtualHost *:80> ServerName www.sprocketsrule.com DocumentRoot "/var/webapps/sprocketsrule/public" <Directory "/var/webapps/sprocketsrule/public"> Options +ExecCGI +FollowSymLinks AddHandler cgi-script .cgi RewriteEngine on RewriteCond /sprockets.js !-f RewriteRule ^sprockets.js /nph-sprockets.cgi [P,L] SetEnv sprockets_generate_output_file true </Directory> </VirtualHost> <script type="text/javascript" src="/sprockets.js"></script>
  41. As a CGI script… <VirtualHost *:80> ServerName www.sprocketsrule.com DocumentRoot "/var/webapps/sprocketsrule/public" <Directory "/var/webapps/sprocketsrule/public"> Options +ExecCGI +FollowSymLinks Provided in the ext/ AddHandler cgi-script .cgi directory of Sprockets RewriteEngine on RewriteCond /sprockets.js !-f RewriteRule ^sprockets.js /nph-sprockets.cgi [P,L] SetEnv sprockets_generate_output_file true </Directory> </VirtualHost> <script type="text/javascript" src="/sprockets.js"></script>
  42. As a CGI script… <VirtualHost *:80> T ServerName www.sprocketsrule.com A DocumentRoot "/var/webapps/sprocketsrule/public" N E <Directory "/var/webapps/sprocketsrule/public"> Options +ExecCGI +FollowSymLinks Provided in the ext/ AddHandler cgi-script .cgi directory of Sprockets RewriteEngine on RewriteCond /sprockets.js !-f RewriteRule ^sprockets.js /nph-sprockets.cgi [P,L] SetEnv sprockets_generate_output_file true </Directory> </VirtualHost> <script type="text/javascript" src="/sprockets.js"></script>
  43. …or use Rails!
  44. …or use Rails! $ script/plugin install git://github.com/sstephenson/sprockets-rails.git
  45. …or use Rails! $ script/plugin install git://github.com/sstephenson/sprockets-rails.git ActionController::Routing::Routes.draw do |map| SprocketsApplication.routes(map) # ... end
  46. …or use Rails! $ script/plugin install git://github.com/sstephenson/sprockets-rails.git ActionController::Routing::Routes.draw do |map| SprocketsApplication.routes(map) # ... end <%= sprockets_include_tag %>
  47. …or use Rails! O M E S $ script/plugin install git://github.com/sstephenson/sprockets-rails.git A W E ActionController::Routing::Routes.draw do |map| SprocketsApplication.routes(map) # ... end <%= sprockets_include_tag %>
  48. Hey, like to squeeze?
  49. Hey, like to squeeze? Just because it’s one big file doesn’t mean it’s one small file (that didn’t come out right)
  50. YUI Compressor
  51. YUI Compressor • Arguably the best source shrinker out there
  52. YUI Compressor • Arguably the best source shrinker out there • 100% safe yet full of optimizations
  53. YUI Compressor • Arguably the best source shrinker out there • 100% safe yet full of optimizations • Better than JSMin, ShrinkSafe, Packer…
  54. YUI Compressor • Arguably the best source shrinker out there • 100% safe yet full of optimizations • Better than JSMin, ShrinkSafe, Packer… • Does not obfuscate (which is good)
  55. YUI Compressor • Arguably the best source shrinker out there • 100% safe yet full of optimizations • Better than JSMin, ShrinkSafe, Packer… • Does not obfuscate (which is good) • Distributed as a JAR file
  56. The gem wrapper
  57. The gem wrapper $ sudo gem install --remote yui-compressor
  58. The gem wrapper $ sudo gem install --remote yui-compressor compressor = YUI::JavaScriptCompressor.new(:munge => true) compressor.compress('(function () { var foo = {}; foo["bar"] = "baz"; })()') # => '(function(){var a={};a.bar="baz"})();'
  59. The gem wrapper $ sudo gem install --remote yui-compressor compressor = YUI::JavaScriptCompressor.new(:munge => true) compressor.compress('(function () { var foo = {}; foo["bar"] = "baz"; })()') # => '(function(){var a={};a.bar="baz"})();' YUI::JavaScriptCompressor.new( :java => "/usr/bin/java", :jar_file => "/path/to/my/yuicompressor-2.4.2.jar" ) Full compressor support: CSS too, options aplenty…
  60. A match made in heaven # config/sprockets.yml :asset_root: public :load_path: - app/javascripts - vendor/sprockets/*/src - vendor/plugins/*/javascripts :source_files: - app/javascripts/application.js - app/javascripts/**/*.js :compress: :munge: true Warning: not in master gems yet. See my forks. (incidentally, in production use at http://letsfreckle.com)
  61. So what’s left?
  62. So what’s left? • Tweak your assets HTTP server: GZip and cache the heck on the client side
  63. So what’s left? • Tweak your assets HTTP server: GZip and cache the heck on the client side • If necessary, offload mainstream libs serving to Google
  64. Apache config you’ll love LoadModule deflate_module /usr/lib/apache2/modules/mod_deflate.so LoadModule headers_module /usr/lib/apache2/modules/mod_headers.so LoadModule expires_module /usr/lib/apache2/modules/mod_expires.so AddOutputFilterByType DEFLATE text/html text/plain text/xml application/xml ↪application/xhtml+xml text/javascript text/css application/x-javascript ExpiresActive On ExpiresByType image/gif "access plus 5 years" ExpiresByType image/png "access plus 5 years" ExpiresByType image/jpeg "access plus 5 years" ExpiresByType text/javascript "access plus 5 years" ExpiresByType application/x-javascript "access plus 5 years" ExpiresByType text/css "access plus 5 years" Header unset ETag FileETag None Header add Cache-Control "public" http://javascriptrocks.com/performance/
  65. Numbers! No opti. All opti. Bytes 292.520 30.919 HTTP requests 3 / 38* 1 Load time (unprimed) 1900+456ms 400+43ms Load time (primed) 1900+76ms 1+41ms * monolithic vendor scripts / structured source
  66. Numbers! No opti. All opti. Bytes 292.520 30.919 HTTP requests 3 / 38* 1 Load time (unprimed) 1900+456ms 400+43ms Load time (primed) 1900+76ms 1+41ms * monolithic vendor scripts / structured source
  67. Numbers! No opti. All opti. Bytes 292.520 30.919 HTTP requests 3 / 38* 1 Load time (unprimed) 1900+456ms 400+43ms Load time (primed) 1900+76ms 1+41ms * monolithic vendor scripts / structured source
  68. Numbers! No opti. All opti. Bytes 292.520 30.919 HTTP requests 3 / 38* 1 Load time (unprimed) 1900+456ms 400+43ms Load time (primed) 1900+76ms 1+41ms * monolithic vendor scripts / structured source
  69. Numbers! No opti. All opti. Bytes 292.520 30.919 HTTP requests 3 / 38* 1 Load time (unprimed) 1900+456ms 400+43ms Load time (primed) 1900+76ms 1+41ms * monolithic vendor scripts / structured source
  70. Numbers! V No opti. All opti. Bytes Y M HTTP requests Load time (unprimed) M 292.520 3 / 38* 1900+456ms 30.919 400+43ms 1 Load time (primed) 1900+76ms 1+41ms * monolithic vendor scripts / structured source
  71. Google Ajax Libraries API
  72. Google Ajax Libraries API • Google hosts most recent versions of Prototype, jQuery, script.aculo.us, MooTools, Dojo, Ext Core,YUI…
  73. Google Ajax Libraries API • Google hosts most recent versions of Prototype, jQuery, script.aculo.us, MooTools, Dojo, Ext Core,YUI… • Top-notch serving (e.g. cache-related headers + GZipping) + CDN
  74. Google Ajax Libraries API • Google hosts most recent versions of Prototype, jQuery, script.aculo.us, MooTools, Dojo, Ext Core,YUI… • Top-notch serving (e.g. cache-related headers + GZipping) + CDN • Bonus: if another site loaded it already, your user doesn’t have to load it for your site!
  75. Thank you, Sam.
  76. Thank you, Sam. • Sprockets, Sprockets-Rails and YUI::Compressor are open- source stuff by Sam Stephenson, creator of Prototype.
  77. Thank you, Sam. • Sprockets, Sprockets-Rails and YUI::Compressor are open- source stuff by Sam Stephenson, creator of Prototype. • http://github.com/sstephenson
  78. Thank you, Sam. • Sprockets, Sprockets-Rails and YUI::Compressor are open- source stuff by Sam Stephenson, creator of Prototype. • http://github.com/sstephenson • http://github.com/tdd
  79. Thank you! Any questions? Christophe Porteneuve tdd@tddsworld.com JSConf.eu 2009

×