Sprockets
 You’ve got tons of JavaScript.
Time to do something about it.


     Christophe Porteneuve
        JSConf.eu 2009
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!
The problem
The problem

• JavaScript is the next big language…
The problem

• JavaScript is the next big language…
• It’s already getting big, codewise!
The problem

• JavaScript is the next big language…
• It’s already getting big, codewise!
• Many apps have countless JS files
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
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…
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
The solution
The solution

• You want to split your JS code in neat files
  and folders!
The solution

• You want to split your JS code in neat files
  and folders!
• You want some structure, dammit!
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!
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
So many cool things…
So many cool things…

• Use require directives and load paths to
  structure your source
So many cool things…

• Use require directives and load paths to
  structure your source

• Preprocess constants!
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
Getting organized
Getting organized




        …
        //= require "lang"
        //= require "ajax"
        //= require "dom"
Getting organized



        //=   require   "dom/dom"
        //=   require   "dom/selector"
        //=   require   "dom/form"
        //=   require   "dom/event"

        Element.addMethods();



        …
        //= require "lang"
        //= require "ajax"
        //= require "dom"
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"
The load path
Sprockets has a load path, much like C’s or
Ruby’s
The load path
Sprockets has a load path, much like C’s or
Ruby’s

Using the load path:
         //= require <coolobj>
         //= require <coollib/coolobj>
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"
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
Constants for you
Constants for you
Constants for you


        PROTOTYPE_VERSION: 1.6.1_rc3
Constants for you


        PROTOTYPE_VERSION: 1.6.1_rc3




        var Prototype = {
          Version: '<%= PROTOTYPE_VERSION %>',

         Browser: (function(){
           …
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)
I can haz?

• It’s a Ruby gem
• You don’t need to know Ruby though
• You do need Ruby and Rubygems installed
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
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
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
Within Ruby code…
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
As a CGI script…
         Say you have this tree
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
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>
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>
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>
…or use Rails!
…or use Rails!

$ script/plugin install git://github.com/sstephenson/sprockets-rails.git
…or use Rails!

$ script/plugin install git://github.com/sstephenson/sprockets-rails.git




ActionController::Routing::Routes.draw do |map|
  SprocketsApplication.routes(map)
  # ...
end
…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 %>
…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 %>
Hey, like to squeeze?
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)
YUI Compressor
YUI Compressor

• Arguably the best source shrinker out
  there
YUI Compressor

• Arguably the best source shrinker out
  there
• 100% safe yet full of optimizations
YUI Compressor

• Arguably the best source shrinker out
  there
• 100% safe yet full of optimizations
• Better than JSMin, ShrinkSafe, Packer…
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)
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
The gem wrapper
The gem wrapper
$ sudo gem install --remote yui-compressor
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"})();'
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…
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)
So what’s left?
So what’s left?


• Tweak your assets HTTP server: GZip and
  cache the heck on the client side
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
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/
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
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
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
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
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
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
Google Ajax Libraries API
Google Ajax Libraries API

• Google hosts most recent versions of
  Prototype, jQuery, script.aculo.us,
  MooTools, Dojo, Ext Core,YUI…
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
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!
Thank you, Sam.
Thank you, Sam.
•   Sprockets, Sprockets-Rails and
    YUI::Compressor are open-
    source stuff by Sam
    Stephenson, creator of
    Prototype.
Thank you, Sam.
•   Sprockets, Sprockets-Rails and
    YUI::Compressor are open-
    source stuff by Sam
    Stephenson, creator of
    Prototype.

•   http://github.com/sstephenson
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
Thank you!
   Any questions?



 Christophe Porteneuve
  tdd@tddsworld.com
     JSConf.eu 2009

Sprockets

  • 1.
    Sprockets You’ve gottons 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.
  • 4.
    The problem • JavaScriptis the next big language…
  • 5.
    The problem • JavaScriptis the next big language… • It’s already getting big, codewise!
  • 6.
    The problem • JavaScriptis the next big language… • It’s already getting big, codewise! • Many apps have countless JS files
  • 7.
    The problem • JavaScriptis the next big language… • It’s already getting big, codewise! • Many apps have countless JS files • Loading → lots of requests → latence
  • 8.
    The problem • JavaScriptis 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 • JavaScriptis 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.
  • 11.
    The solution • Youwant to split your JS code in neat files and folders!
  • 12.
    The solution • Youwant to split your JS code in neat files and folders! • You want some structure, dammit!
  • 13.
    The solution • Youwant 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 • Youwant 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 coolthings…
  • 16.
    So many coolthings… • Use require directives and load paths to structure your source
  • 17.
    So many coolthings… • Use require directives and load paths to structure your source • Preprocess constants!
  • 18.
    So many coolthings… • 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.
  • 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 Sprocketshas a load path, much like C’s or Ruby’s
  • 24.
    The load path Sprocketshas a load path, much like C’s or Ruby’s Using the load path: //= require <coolobj> //= require <coollib/coolobj>
  • 25.
    The load path Sprocketshas 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 aconstants.yml file somewhere in your load path • It’s a YAML file • Heard of YAML, right? • Super simple yet powerful
  • 27.
  • 28.
  • 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-linetool… $ 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-linetool… $ 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.
  • 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 CGIscript… Say you have this tree
  • 39.
    As a CGIscript… 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 CGIscript… <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 CGIscript… <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 CGIscript… <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.
  • 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 tosqueeze?
  • 49.
    Hey, like tosqueeze? Just because it’s one big file doesn’t mean it’s one small file (that didn’t come out right)
  • 50.
  • 51.
    YUI Compressor • Arguablythe best source shrinker out there
  • 52.
    YUI Compressor • Arguablythe best source shrinker out there • 100% safe yet full of optimizations
  • 53.
    YUI Compressor • Arguablythe best source shrinker out there • 100% safe yet full of optimizations • Better than JSMin, ShrinkSafe, Packer…
  • 54.
    YUI Compressor • Arguablythe 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 • Arguablythe 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.
  • 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 madein 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.
  • 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’lllove 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.
  • 72.
    Google Ajax LibrariesAPI • Google hosts most recent versions of Prototype, jQuery, script.aculo.us, MooTools, Dojo, Ext Core,YUI…
  • 73.
    Google Ajax LibrariesAPI • 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 LibrariesAPI • 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.
  • 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
  • 80.
    Thank you! Any questions? Christophe Porteneuve tdd@tddsworld.com JSConf.eu 2009