SlideShare una empresa de Scribd logo
1 de 70
Meetup Performance
    Justin Cataldo, Lead UI Engineer (@jcataldo)
Why do we care?
Why do we care?
     Slow Site
Why do we care?
       Slow Site


  Bad User Experience
Why do we care?
        Slow Site


  Bad User Experience


 Drop in Member Activity
       (Less people meeting up)
So what should we do?
Reduce as much as
    possible!
But what are we reducing?
But what are we reducing?

 • JavaScript requests
But what are we reducing?

 • JavaScript requests
 • Image requests
But what are we reducing?

 • JavaScript requests
 • Image requests
 • DOM
But what are we reducing?

 •   JavaScript requests
 •   Image requests
 •   DOM
 •   CSS
Reducing JavaScript

• Externalize
• Concatenate
• Load only what you need upfront
• Position accordingly
Blast from the past
    (November 2009)
Pre JS Reduction
   (November 2009)
Why externalize?

• Inline scripts block
• Lose the benefit of caching
• Code reusability
Concatenate

• Sprockets (www.getsprockets.com)
• Ruby library that preprocesses and
  concatenates JavaScript files
• Baked into our build process
How Sprockets works

//******
// Sprockets Directive inside details.js
//******
//= require <templates/Meetup>
//= require <Meetup/Validator>
//= require <plug-in/lazyImage>            ANT process   EventDetails.js
//= require <plug-in/expando>
//= require <plug-in/actionDropdown>
//= require <Meetup/i18N>
...
How Sprockets works


/******* Build Process *******/
<exec executable="sprocketize" failonerror="true" output="${image.dir}/script/Meetup/packed/EventDetails.js">
   <arg value="-I"/>
   <arg path="${image.dir}/script/Meetup/"/>
   <arg path="${image.dir}/script/jquery/Meetup/Event/details.js"/>
</exec>
One gotcha
Caching
        Split your files up!

You will lose the benefit of caching
 if constantly changing a large file
Lazy Load

• Defer the loading of JavaScript files until
  they are needed
• Reduces the initial upfront requests
• Allows files to download asynchronously
• Use it to precache
How it works
  LABjs (www.labjs.com) example

<script>
   $LAB
   .script("framework.js").wait()
   .script("plugin.framework.js")
   .script("myplugin.framework.js")
   .wait(function(){
       myplugin.init();
       framework.init();
       framework.doSomething();
   });
</script>
Defer loading and precache
            I don’t need this every time I visit

           On first visit, lazy load the file in so I
            have it in cache for when I need it

           Load/execute the JavaScript when I
                   click on the link
Scripts at the bottom

• Custom tag called web:script
• Moves all scripts to the bottom
• Allows for lazy loading
• Compresses inline scripts using
  YUICompressor
web:script


   /***** Load External Script *****/
<web:script src="/script/Meetup/packed/EventDetails.js" />

/***** Load Inline Script *****/
<web:script>
      Meetup.Copy.noMembersMarkedAttended = "<trn:message key="event.attendance.noMembersMarkedAttended">No
members have been marked attended</trn:message>";
      Meetup.Copy.noMembersMarkedAttendedDynam = '<trn:message
key="event.attendance.noMembersMarkedAttendedDynam"><trn:param name="GROUPING">__GROUPING__</
trn:param>No members in "{GROUPING}" have been marked attended</trn:message>';
      Meetup.Copy.noMembersMarkedAbsent = "<trn:message key="event.attendance.noMembersMarkedAbsent">No
members have been marked absent</trn:message>";
</web:script>
If you can’t move to the
    bottom, lazy load
Post JS Reduction
    (November 2009)
Post JS Reduction
     (January 2011)
Defer images
Lazy loading for images
Do you really need all those photos to load?

  • Only load what’s in the viewport
  • Load the rest later
    •   On scroll

    •   After page loaded
How it works
    return elements.each(function () {


   
  
   var self = $(this),

   
  
   
   src = self.attr('data-src'); // We set a data attribute for the image

   
  
   self.one('appear',function() {

   
  
   
   $('<img />').bind('load', function() {

   
  
   
   
   self.hide().attr('src', src)[options.effect]
(options.effectspeed);

   
  
   
   
   self.data('loaded', true);

   
  
   
   
   var temp = $.grep(elements, function (item) {

   
  
   
   
   
   return !$(item).data('loaded');

   
  
   
   
   });

   
  
   
   
   if (temp.length == 0) {

   
  
   
   
   
   $window.unbind('scroll', load);

   
  
   
   
   }

   
  
   
   }).attr('src', src);

   
  
   });

   
  
   if (!isBelowFold(self, options)) {

   
  
   
   self.trigger('appear'); // In viewport then show

   
  
   } else {

   
  
   
   self.data('loaded', false); // Else set loaded to false

   
  
   }

   });
Embed images
                 (If you can)




• Use Data-uri/MHTML
• Embeds the images in your code
• Reduces requests
 • but increases file size
Use vector images
                  (If you can)




• Supported in all browsers
• raphael.js makes it easy
• Uses SVG and VML
Oh and, Smush those images!
Now tame that DOM
Reduce your DOM

• Larger the page, the longer it takes to
  download
• Heavily nested elements take longer to
  render
• DOM and CSS go hand and hand
http://mir.aculo.us/dom-monster/
DOM Monster
“DOM Monster is a cross-platform, cross-
browser bookmarklet that will analyze the
DOM & other features of the page you're on,
and give you its bill of health.”
We have a case of divitis and
 DOM Monster is the cure
Clean up your CSS

• Remove unused CSS
• Use efficient selectors
• Reduce CSS
Write efficient selectors
•   Avoid a universal selector
     • Uses classes or allow elements to inherit from ancestors
•   Make your rules as specific as possible
     • Use classes or IDs over tag selectors, allows for less traversal
•   Remove redundant qualifiers
     • body ul li a {...} - everything is always under body so we don’t need it
     • form#myForm {...} ---> #myForm {...}
•   Use classes instead of descendant selectors
     • ul li {color: red} ---> .list-item-red {color:red}
•   Avoid :hover on non-link elements for IE
     • Use JS mouseover

     http://code.google.com/speed/page-speed/docs/rendering.html#UseEfficientCSSSelectors
Use PageSpeed




  http://code.google.com/speed/page-speed/
Reducing is a start,
  so now what?
Profile your code
    Every ms count
Tools for profiling

• Firebug - Firefox
• DynaTrace AJAX Edition - IE
• Web Inspector - Chrome/Safari
Speed up that JavaScript
Let’s optimize this


function renderComments(data) {

     for(var i = 0; i < data.length; i++) {
          var comment = $(‘<li class=”comment” id=”’+data[i].id+’”>‘+data[i].comment+’</li>’);
          $(“#commentList”).append(comment);
     };

};
Time it
function renderComments(data) {
    console.time(“myloop”);
    for(var i = 0; i < data.length; i++) {
          var comment = $(‘<li class=”comment” id=”’+data[i].id+’”>‘+data[i].comment+’</li>’);
          $(“#commentList”).append(comment);
    };
    console.timeEnd(“myloop”);
};
Time it
function renderComments(data) {
    console.time(“myloop”);
    for(var i = 0; i < data.length; i++) {
          var comment = $(‘<li class=”comment” id=”’+data[i].id+’”>‘+data[i].comment+’</li>’);
          $(“#commentList”).append(comment);
    };
    console.timeEnd(“myloop”);
};




                                                  3ms
Optimize
function renderComments(data) {
    console.time(“myloop”);
    var i = 0,
         length = data.length;

     for(i; i < length; i++) { // Evaluates data.length every time
           var comment = $(‘<li class=”comment” id=”’+data[i].id+’”>‘+data[i].comment+’</li>’);
           $(“#commentList”).append(comment);
     };
     console.timeEnd(“myloop”);
};
Optimize
function renderComments(data) {
    console.time(“myloop”);
    var i = 0,
         length = data.length;

     for(i; i < length; i++) { // Evaluates data.length every time
           var comment = $(‘<li class=”comment” id=”’+data[i].id+’”>‘+data[i].comment+’</li>’);
           $(“#commentList”).append(comment);
     };
     console.timeEnd(“myloop”);
};




                                                  2ms
Stop hitting the DOM!
function renderComments(data) {
    console.time(“myloop”);
    var i = 0,
         length = data.length;

     for(i; i < length; i++) {
           var comment = $(‘<li class=”comment” id=”’+data[i].id+’”>‘+data[i].comment+’</li>’);
           $(“#commentList”).append(comment); // BAD
     };
     console.timeEnd(“myloop”);
};
Stop hitting the DOM!
function renderComments(data) {
    console.time(“myloop”);
    var i = 0,
         length = data.length,
         frag = document.createDocumentFragment();

     for(i; i < length; i++) {
           // Append off the DOM
           frag.appendChild($(‘<li class=”comment” id=”’+data[i].id+’”>‘+data[i].comment+’</li>’));
     };

     $(“#commentList”).append(frag); // GOOD
     console.timeEnd(“myloop”);
};
Stop hitting the DOM!
function renderComments(data) {
    console.time(“myloop”);
    var i = 0,
         length = data.length,
         frag = document.createDocumentFragment();

     for(i; i < length; i++) {
           // Append off the DOM
           frag.appendChild($(‘<li class=”comment” id=”’+data[i].id+’”>‘+data[i].comment+’</li>’));
     };

     $(“#commentList”).append(frag); // GOOD
     console.timeEnd(“myloop”);
};




                                                   2ms
Use a templating framework
Mustache

       • Use on the client or server side
       • Makes generating html simple
       • Logic-less

                                 http://mustache.github.com/
http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-using-the-mustache-template-library/
Your template
var commentTemplate = '{{#results}}<li class="feed-item-small clearfix last" id="comment_{{id}}_{{type}}">
           <a href="{{url}}" class="mem-photo-small" title="{{name}}"><img src="{{photo}}" alt="{{name}}" /></a>
           <div class="feed-item-content-small">
                <a href="{{url}}" title="{{name}}">{{name}}</a>
                <p>{{#dontescape}}{{{comment}}}{{/dontescape}}{{^dontescape}}{{comment}}{{/dontescape}}</p>
                <div class="feed-item-action D_empty D_less">
                      Posted {{posted}} <span id="likewidget_{{id}}" class="commentCountBadge">{{{likes}}}</
span> {{#ismember}}{{{liked}}} {{#iscomment}}| <a href="#" class="deleter" title="Delete this comment"
id="delete_{{id}}_{{type}}">Delete</a>{{/iscomment}}{{/ismember}}
                </div>
           </div>
   </li>{{/results}}';
Your data
var view = {
      results: {
          name: item.member_name,
          url: Chapter.groupUrl + "members/" + item.member_id,
          photo: item.photo_url,
          comment: item.comment_text,
          id: item.id,
          table: item.table_name,
          posted: item.date,
          likes: function() {
                 var length = item.likes.length;
                 return (length > 0 ? '| <a href="#" class="likedialog" data-type="' + item.table_name + '" id="likedialog_' +
item.id + '"><span class="bold">' + length + ' likes</span></a> |' : '|');
          },
          liked: function() {
                 var ids = $.map(item.likes, function(id) {
                      return id.member_id;
                 });
                 return $.inArray(Member.id, ids) > -1 ? '<a href="#" class="cvoter" data-type="' + item.table_name + '"
title="Unlike this comment" id="cvoter_' + item.id + '">Unlike</a>' : '<a href="#" class="cvoter" data-type="' +
item.table_name + '" title="Like this comment" id="cvoter_' + item.id + '">Like</a>';
          },
          type: item.is_suggestion_comment ? "idea" : "event",
          ismember: Member.isMember,
          iscomment: Member.id === item.member_id && item.table_name != 'event_diff',
          dontescape: item.table_name == 'event_diff'
      }
};
Put it together


var comment = $.mustache(commentTemplate, view);

        You now have generated html you
            can insert into the DOM
Tip: Use event delegation with templates or don’t
                forget to unbind!
    $(“#myid”).delegate(“.someclass”,”click”,function{...});
Quick tips
Cache variables

Don’t re-evaluate throughout the code

var savedGuestCount = 0,
         savedPolicyState = false,
         suggestParamValue = null, //'date' or 'venue'
         isLoaded = false,
         hours12 = ["7", "8", "9", "10", "11", "12", "1", "2", "3", "4", "5", "6"],
         hours24 = ["19", "20", "21", "22", "23", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13",
"14", "15", "16", "17", "18"],
         minutes = ["00", "15", "30", "45"],
         evtdets = $("#eventdets"),
         templates = Meetup.Templates,.....
Add context to selectors and cache them

Context
$("a.clickme").addClass("red"); //Not bad

$("#container").find("a.clickme").addClass("red"); //Better
$("#container a.clickme").addClass("red"); //Better


Cache Selectors
var clickmes = $("#container").find("a.clickme");

clickmes.addClass("red");
So what’s the outcome?
Waterfall
(November 2009)




         Thats a big waterfall

                  4.643s
Waterfall
(January 2011)




                 Thats better

                   3.826s

        18% decrease in load time
Read!



• JavaScript Performance Rocks!
• High Performance Web Sites
• Even Faster Web Sites
• JavaScript Patterns
Thanks
Email:       justin@meetup.com
Twitter:     @jcataldo
LinkedIn:     linkedin.com/in/jcataldo

Más contenido relacionado

La actualidad más candente

The DOM is a Mess @ Yahoo
The DOM is a Mess @ YahooThe DOM is a Mess @ Yahoo
The DOM is a Mess @ Yahoojeresig
 
modern module development - Ken Barber 2012 Edinburgh Puppet Camp
modern module development - Ken Barber 2012 Edinburgh Puppet Campmodern module development - Ken Barber 2012 Edinburgh Puppet Camp
modern module development - Ken Barber 2012 Edinburgh Puppet CampPuppet
 
#NoXML: Eliminating XML in Spring Projects - SpringOne 2GX 2015
#NoXML: Eliminating XML in Spring Projects - SpringOne 2GX 2015#NoXML: Eliminating XML in Spring Projects - SpringOne 2GX 2015
#NoXML: Eliminating XML in Spring Projects - SpringOne 2GX 2015Matt Raible
 
Instant and offline apps with Service Worker
Instant and offline apps with Service WorkerInstant and offline apps with Service Worker
Instant and offline apps with Service WorkerChang W. Doh
 
Eddystone Beacons - Physical Web - Giving a URL to All Objects
Eddystone Beacons - Physical Web - Giving a URL to All ObjectsEddystone Beacons - Physical Web - Giving a URL to All Objects
Eddystone Beacons - Physical Web - Giving a URL to All ObjectsJeff Prestes
 
The Art of AngularJS in 2015 - Angular Summit 2015
The Art of AngularJS in 2015 - Angular Summit 2015The Art of AngularJS in 2015 - Angular Summit 2015
The Art of AngularJS in 2015 - Angular Summit 2015Matt Raible
 
Build Your Own CMS with Apache Sling
Build Your Own CMS with Apache SlingBuild Your Own CMS with Apache Sling
Build Your Own CMS with Apache SlingBob Paulin
 
jQuery Proven Performance Tips & Tricks
jQuery Proven Performance Tips & TricksjQuery Proven Performance Tips & Tricks
jQuery Proven Performance Tips & TricksAddy Osmani
 
Hyperlight Websites - Chris Zacharias
Hyperlight Websites - Chris ZachariasHyperlight Websites - Chris Zacharias
Hyperlight Websites - Chris ZachariasChristopher Zacharias
 
Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점
Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점
Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점Jeado Ko
 
Bootstrap과 UI-Bootstrap
Bootstrap과 UI-BootstrapBootstrap과 UI-Bootstrap
Bootstrap과 UI-BootstrapWebFrameworks
 
High Performance JavaScript - WebDirections USA 2010
High Performance JavaScript - WebDirections USA 2010High Performance JavaScript - WebDirections USA 2010
High Performance JavaScript - WebDirections USA 2010Nicholas Zakas
 
Progressive Enhancement 2.0 (jQuery Conference SF Bay Area 2011)
Progressive Enhancement 2.0 (jQuery Conference SF Bay Area 2011)Progressive Enhancement 2.0 (jQuery Conference SF Bay Area 2011)
Progressive Enhancement 2.0 (jQuery Conference SF Bay Area 2011)Nicholas Zakas
 
Progressive Enhancement 2.0 (Conference Agnostic)
Progressive Enhancement 2.0 (Conference Agnostic)Progressive Enhancement 2.0 (Conference Agnostic)
Progressive Enhancement 2.0 (Conference Agnostic)Nicholas Zakas
 
Browsers with Wings
Browsers with WingsBrowsers with Wings
Browsers with WingsRemy Sharp
 
HTML5 vs Silverlight
HTML5 vs SilverlightHTML5 vs Silverlight
HTML5 vs SilverlightMatt Casto
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricksJavier Eguiluz
 

La actualidad más candente (20)

The DOM is a Mess @ Yahoo
The DOM is a Mess @ YahooThe DOM is a Mess @ Yahoo
The DOM is a Mess @ Yahoo
 
modern module development - Ken Barber 2012 Edinburgh Puppet Camp
modern module development - Ken Barber 2012 Edinburgh Puppet Campmodern module development - Ken Barber 2012 Edinburgh Puppet Camp
modern module development - Ken Barber 2012 Edinburgh Puppet Camp
 
#NoXML: Eliminating XML in Spring Projects - SpringOne 2GX 2015
#NoXML: Eliminating XML in Spring Projects - SpringOne 2GX 2015#NoXML: Eliminating XML in Spring Projects - SpringOne 2GX 2015
#NoXML: Eliminating XML in Spring Projects - SpringOne 2GX 2015
 
Ajax Security
Ajax SecurityAjax Security
Ajax Security
 
Instant and offline apps with Service Worker
Instant and offline apps with Service WorkerInstant and offline apps with Service Worker
Instant and offline apps with Service Worker
 
Eddystone Beacons - Physical Web - Giving a URL to All Objects
Eddystone Beacons - Physical Web - Giving a URL to All ObjectsEddystone Beacons - Physical Web - Giving a URL to All Objects
Eddystone Beacons - Physical Web - Giving a URL to All Objects
 
The Art of AngularJS in 2015 - Angular Summit 2015
The Art of AngularJS in 2015 - Angular Summit 2015The Art of AngularJS in 2015 - Angular Summit 2015
The Art of AngularJS in 2015 - Angular Summit 2015
 
Build Your Own CMS with Apache Sling
Build Your Own CMS with Apache SlingBuild Your Own CMS with Apache Sling
Build Your Own CMS with Apache Sling
 
jQuery Proven Performance Tips & Tricks
jQuery Proven Performance Tips & TricksjQuery Proven Performance Tips & Tricks
jQuery Proven Performance Tips & Tricks
 
Hyperlight Websites - Chris Zacharias
Hyperlight Websites - Chris ZachariasHyperlight Websites - Chris Zacharias
Hyperlight Websites - Chris Zacharias
 
Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점
Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점
Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점
 
SocketStream
SocketStreamSocketStream
SocketStream
 
Bootstrap과 UI-Bootstrap
Bootstrap과 UI-BootstrapBootstrap과 UI-Bootstrap
Bootstrap과 UI-Bootstrap
 
High Performance JavaScript - WebDirections USA 2010
High Performance JavaScript - WebDirections USA 2010High Performance JavaScript - WebDirections USA 2010
High Performance JavaScript - WebDirections USA 2010
 
jQuery UI and Plugins
jQuery UI and PluginsjQuery UI and Plugins
jQuery UI and Plugins
 
Progressive Enhancement 2.0 (jQuery Conference SF Bay Area 2011)
Progressive Enhancement 2.0 (jQuery Conference SF Bay Area 2011)Progressive Enhancement 2.0 (jQuery Conference SF Bay Area 2011)
Progressive Enhancement 2.0 (jQuery Conference SF Bay Area 2011)
 
Progressive Enhancement 2.0 (Conference Agnostic)
Progressive Enhancement 2.0 (Conference Agnostic)Progressive Enhancement 2.0 (Conference Agnostic)
Progressive Enhancement 2.0 (Conference Agnostic)
 
Browsers with Wings
Browsers with WingsBrowsers with Wings
Browsers with Wings
 
HTML5 vs Silverlight
HTML5 vs SilverlightHTML5 vs Silverlight
HTML5 vs Silverlight
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricks
 

Destacado

Rebeca Suau
Rebeca SuauRebeca Suau
Rebeca Suaujferre43
 
R Gould Portfolio Samples
R Gould Portfolio SamplesR Gould Portfolio Samples
R Gould Portfolio Samplesguestdb8d8
 
Practical Tips to Grow Your Mobile Audience
Practical Tips to Grow Your Mobile AudiencePractical Tips to Grow Your Mobile Audience
Practical Tips to Grow Your Mobile AudienceMartin Shen
 
Klutch Messenger for Real Life Meetups
Klutch Messenger for Real Life MeetupsKlutch Messenger for Real Life Meetups
Klutch Messenger for Real Life MeetupsHunter Gray
 
Tecniques ArtíStiques 4 Eso 2010
Tecniques ArtíStiques 4 Eso 2010Tecniques ArtíStiques 4 Eso 2010
Tecniques ArtíStiques 4 Eso 2010jferre43
 

Destacado (7)

Rebeca Suau
Rebeca SuauRebeca Suau
Rebeca Suau
 
R Gould Portfolio Samples
R Gould Portfolio SamplesR Gould Portfolio Samples
R Gould Portfolio Samples
 
Practical Tips to Grow Your Mobile Audience
Practical Tips to Grow Your Mobile AudiencePractical Tips to Grow Your Mobile Audience
Practical Tips to Grow Your Mobile Audience
 
op 3eso
op 3esoop 3eso
op 3eso
 
Klutch Messenger for Real Life Meetups
Klutch Messenger for Real Life MeetupsKlutch Messenger for Real Life Meetups
Klutch Messenger for Real Life Meetups
 
Meetup Performance
Meetup PerformanceMeetup Performance
Meetup Performance
 
Tecniques ArtíStiques 4 Eso 2010
Tecniques ArtíStiques 4 Eso 2010Tecniques ArtíStiques 4 Eso 2010
Tecniques ArtíStiques 4 Eso 2010
 

Similar a #NewMeetup Performance

Javascript first-class citizenery
Javascript first-class citizeneryJavascript first-class citizenery
Javascript first-class citizenerytoddbr
 
SharePoint Cincy 2012 - jQuery essentials
SharePoint Cincy 2012 - jQuery essentialsSharePoint Cincy 2012 - jQuery essentials
SharePoint Cincy 2012 - jQuery essentialsMark Rackley
 
HTML5 New and Improved
HTML5   New and ImprovedHTML5   New and Improved
HTML5 New and ImprovedTimothy Fisher
 
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)Igor Bronovskyy
 
Performance Optimization and JavaScript Best Practices
Performance Optimization and JavaScript Best PracticesPerformance Optimization and JavaScript Best Practices
Performance Optimization and JavaScript Best PracticesDoris Chen
 
Enhance Web Performance
Enhance Web PerformanceEnhance Web Performance
Enhance Web PerformanceAdam Lu
 
Ajax Performance Tuning and Best Practices
Ajax Performance Tuning and Best PracticesAjax Performance Tuning and Best Practices
Ajax Performance Tuning and Best PracticesDoris Chen
 
WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...Fabio Franzini
 
SharePoint and jQuery Essentials
SharePoint and jQuery EssentialsSharePoint and jQuery Essentials
SharePoint and jQuery EssentialsMark Rackley
 
Being a tweaker modern web performance techniques
Being a tweaker   modern web performance techniquesBeing a tweaker   modern web performance techniques
Being a tweaker modern web performance techniquesChris Love
 
[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVCAlive Kuo
 
JavaScript performance patterns
JavaScript performance patternsJavaScript performance patterns
JavaScript performance patternsStoyan Stefanov
 
JavaScript Performance Patterns
JavaScript Performance PatternsJavaScript Performance Patterns
JavaScript Performance PatternsStoyan Stefanov
 
SPTechCon DevDays - SharePoint & jQuery
SPTechCon DevDays - SharePoint & jQuerySPTechCon DevDays - SharePoint & jQuery
SPTechCon DevDays - SharePoint & jQueryMark Rackley
 
The Magic Revealed: Four Real-World Examples of Using the Client Object Model...
The Magic Revealed: Four Real-World Examples of Using the Client Object Model...The Magic Revealed: Four Real-World Examples of Using the Client Object Model...
The Magic Revealed: Four Real-World Examples of Using the Client Object Model...SPTechCon
 

Similar a #NewMeetup Performance (20)

Performance patterns
Performance patternsPerformance patterns
Performance patterns
 
Javascript first-class citizenery
Javascript first-class citizeneryJavascript first-class citizenery
Javascript first-class citizenery
 
SharePoint Cincy 2012 - jQuery essentials
SharePoint Cincy 2012 - jQuery essentialsSharePoint Cincy 2012 - jQuery essentials
SharePoint Cincy 2012 - jQuery essentials
 
HTML5 New and Improved
HTML5   New and ImprovedHTML5   New and Improved
HTML5 New and Improved
 
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
 
Performance Optimization and JavaScript Best Practices
Performance Optimization and JavaScript Best PracticesPerformance Optimization and JavaScript Best Practices
Performance Optimization and JavaScript Best Practices
 
Jquery fundamentals
Jquery fundamentalsJquery fundamentals
Jquery fundamentals
 
Enhance Web Performance
Enhance Web PerformanceEnhance Web Performance
Enhance Web Performance
 
Ajax Performance Tuning and Best Practices
Ajax Performance Tuning and Best PracticesAjax Performance Tuning and Best Practices
Ajax Performance Tuning and Best Practices
 
AD102 - Break out of the Box
AD102 - Break out of the BoxAD102 - Break out of the Box
AD102 - Break out of the Box
 
WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...
 
前端概述
前端概述前端概述
前端概述
 
SharePoint and jQuery Essentials
SharePoint and jQuery EssentialsSharePoint and jQuery Essentials
SharePoint and jQuery Essentials
 
Being a tweaker modern web performance techniques
Being a tweaker   modern web performance techniquesBeing a tweaker   modern web performance techniques
Being a tweaker modern web performance techniques
 
[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC
 
JavaScript performance patterns
JavaScript performance patternsJavaScript performance patterns
JavaScript performance patterns
 
Dartprogramming
DartprogrammingDartprogramming
Dartprogramming
 
JavaScript Performance Patterns
JavaScript Performance PatternsJavaScript Performance Patterns
JavaScript Performance Patterns
 
SPTechCon DevDays - SharePoint & jQuery
SPTechCon DevDays - SharePoint & jQuerySPTechCon DevDays - SharePoint & jQuery
SPTechCon DevDays - SharePoint & jQuery
 
The Magic Revealed: Four Real-World Examples of Using the Client Object Model...
The Magic Revealed: Four Real-World Examples of Using the Client Object Model...The Magic Revealed: Four Real-World Examples of Using the Client Object Model...
The Magic Revealed: Four Real-World Examples of Using the Client Object Model...
 

#NewMeetup Performance

  • 1. Meetup Performance Justin Cataldo, Lead UI Engineer (@jcataldo)
  • 2. Why do we care?
  • 3. Why do we care? Slow Site
  • 4. Why do we care? Slow Site Bad User Experience
  • 5. Why do we care? Slow Site Bad User Experience Drop in Member Activity (Less people meeting up)
  • 6. So what should we do?
  • 7. Reduce as much as possible!
  • 8. But what are we reducing?
  • 9. But what are we reducing? • JavaScript requests
  • 10. But what are we reducing? • JavaScript requests • Image requests
  • 11. But what are we reducing? • JavaScript requests • Image requests • DOM
  • 12. But what are we reducing? • JavaScript requests • Image requests • DOM • CSS
  • 13. Reducing JavaScript • Externalize • Concatenate • Load only what you need upfront • Position accordingly
  • 14. Blast from the past (November 2009)
  • 15. Pre JS Reduction (November 2009)
  • 16. Why externalize? • Inline scripts block • Lose the benefit of caching • Code reusability
  • 17. Concatenate • Sprockets (www.getsprockets.com) • Ruby library that preprocesses and concatenates JavaScript files • Baked into our build process
  • 18. How Sprockets works //****** // Sprockets Directive inside details.js //****** //= require <templates/Meetup> //= require <Meetup/Validator> //= require <plug-in/lazyImage> ANT process EventDetails.js //= require <plug-in/expando> //= require <plug-in/actionDropdown> //= require <Meetup/i18N> ...
  • 19. How Sprockets works /******* Build Process *******/ <exec executable="sprocketize" failonerror="true" output="${image.dir}/script/Meetup/packed/EventDetails.js"> <arg value="-I"/> <arg path="${image.dir}/script/Meetup/"/> <arg path="${image.dir}/script/jquery/Meetup/Event/details.js"/> </exec>
  • 21. Caching Split your files up! You will lose the benefit of caching if constantly changing a large file
  • 22. Lazy Load • Defer the loading of JavaScript files until they are needed • Reduces the initial upfront requests • Allows files to download asynchronously • Use it to precache
  • 23. How it works LABjs (www.labjs.com) example <script> $LAB .script("framework.js").wait() .script("plugin.framework.js") .script("myplugin.framework.js") .wait(function(){ myplugin.init(); framework.init(); framework.doSomething(); }); </script>
  • 24. Defer loading and precache I don’t need this every time I visit On first visit, lazy load the file in so I have it in cache for when I need it Load/execute the JavaScript when I click on the link
  • 25. Scripts at the bottom • Custom tag called web:script • Moves all scripts to the bottom • Allows for lazy loading • Compresses inline scripts using YUICompressor
  • 26. web:script /***** Load External Script *****/ <web:script src="/script/Meetup/packed/EventDetails.js" /> /***** Load Inline Script *****/ <web:script> Meetup.Copy.noMembersMarkedAttended = "<trn:message key="event.attendance.noMembersMarkedAttended">No members have been marked attended</trn:message>"; Meetup.Copy.noMembersMarkedAttendedDynam = '<trn:message key="event.attendance.noMembersMarkedAttendedDynam"><trn:param name="GROUPING">__GROUPING__</ trn:param>No members in "{GROUPING}" have been marked attended</trn:message>'; Meetup.Copy.noMembersMarkedAbsent = "<trn:message key="event.attendance.noMembersMarkedAbsent">No members have been marked absent</trn:message>"; </web:script>
  • 27. If you can’t move to the bottom, lazy load
  • 28. Post JS Reduction (November 2009)
  • 29. Post JS Reduction (January 2011)
  • 31. Do you really need all those photos to load? • Only load what’s in the viewport • Load the rest later • On scroll • After page loaded
  • 32. How it works return elements.each(function () { var self = $(this), src = self.attr('data-src'); // We set a data attribute for the image self.one('appear',function() { $('<img />').bind('load', function() { self.hide().attr('src', src)[options.effect] (options.effectspeed); self.data('loaded', true); var temp = $.grep(elements, function (item) { return !$(item).data('loaded'); }); if (temp.length == 0) { $window.unbind('scroll', load); } }).attr('src', src); }); if (!isBelowFold(self, options)) { self.trigger('appear'); // In viewport then show } else { self.data('loaded', false); // Else set loaded to false } });
  • 33. Embed images (If you can) • Use Data-uri/MHTML • Embeds the images in your code • Reduces requests • but increases file size
  • 34. Use vector images (If you can) • Supported in all browsers • raphael.js makes it easy • Uses SVG and VML
  • 35. Oh and, Smush those images!
  • 37. Reduce your DOM • Larger the page, the longer it takes to download • Heavily nested elements take longer to render • DOM and CSS go hand and hand
  • 39. DOM Monster “DOM Monster is a cross-platform, cross- browser bookmarklet that will analyze the DOM & other features of the page you're on, and give you its bill of health.”
  • 40.
  • 41. We have a case of divitis and DOM Monster is the cure
  • 42. Clean up your CSS • Remove unused CSS • Use efficient selectors • Reduce CSS
  • 43. Write efficient selectors • Avoid a universal selector • Uses classes or allow elements to inherit from ancestors • Make your rules as specific as possible • Use classes or IDs over tag selectors, allows for less traversal • Remove redundant qualifiers • body ul li a {...} - everything is always under body so we don’t need it • form#myForm {...} ---> #myForm {...} • Use classes instead of descendant selectors • ul li {color: red} ---> .list-item-red {color:red} • Avoid :hover on non-link elements for IE • Use JS mouseover http://code.google.com/speed/page-speed/docs/rendering.html#UseEfficientCSSSelectors
  • 44. Use PageSpeed http://code.google.com/speed/page-speed/
  • 45. Reducing is a start, so now what?
  • 46. Profile your code Every ms count
  • 47. Tools for profiling • Firebug - Firefox • DynaTrace AJAX Edition - IE • Web Inspector - Chrome/Safari
  • 48. Speed up that JavaScript
  • 49. Let’s optimize this function renderComments(data) { for(var i = 0; i < data.length; i++) { var comment = $(‘<li class=”comment” id=”’+data[i].id+’”>‘+data[i].comment+’</li>’); $(“#commentList”).append(comment); }; };
  • 50. Time it function renderComments(data) { console.time(“myloop”); for(var i = 0; i < data.length; i++) { var comment = $(‘<li class=”comment” id=”’+data[i].id+’”>‘+data[i].comment+’</li>’); $(“#commentList”).append(comment); }; console.timeEnd(“myloop”); };
  • 51. Time it function renderComments(data) { console.time(“myloop”); for(var i = 0; i < data.length; i++) { var comment = $(‘<li class=”comment” id=”’+data[i].id+’”>‘+data[i].comment+’</li>’); $(“#commentList”).append(comment); }; console.timeEnd(“myloop”); }; 3ms
  • 52. Optimize function renderComments(data) { console.time(“myloop”); var i = 0, length = data.length; for(i; i < length; i++) { // Evaluates data.length every time var comment = $(‘<li class=”comment” id=”’+data[i].id+’”>‘+data[i].comment+’</li>’); $(“#commentList”).append(comment); }; console.timeEnd(“myloop”); };
  • 53. Optimize function renderComments(data) { console.time(“myloop”); var i = 0, length = data.length; for(i; i < length; i++) { // Evaluates data.length every time var comment = $(‘<li class=”comment” id=”’+data[i].id+’”>‘+data[i].comment+’</li>’); $(“#commentList”).append(comment); }; console.timeEnd(“myloop”); }; 2ms
  • 54. Stop hitting the DOM! function renderComments(data) { console.time(“myloop”); var i = 0, length = data.length; for(i; i < length; i++) { var comment = $(‘<li class=”comment” id=”’+data[i].id+’”>‘+data[i].comment+’</li>’); $(“#commentList”).append(comment); // BAD }; console.timeEnd(“myloop”); };
  • 55. Stop hitting the DOM! function renderComments(data) { console.time(“myloop”); var i = 0, length = data.length, frag = document.createDocumentFragment(); for(i; i < length; i++) { // Append off the DOM frag.appendChild($(‘<li class=”comment” id=”’+data[i].id+’”>‘+data[i].comment+’</li>’)); }; $(“#commentList”).append(frag); // GOOD console.timeEnd(“myloop”); };
  • 56. Stop hitting the DOM! function renderComments(data) { console.time(“myloop”); var i = 0, length = data.length, frag = document.createDocumentFragment(); for(i; i < length; i++) { // Append off the DOM frag.appendChild($(‘<li class=”comment” id=”’+data[i].id+’”>‘+data[i].comment+’</li>’)); }; $(“#commentList”).append(frag); // GOOD console.timeEnd(“myloop”); }; 2ms
  • 57. Use a templating framework
  • 58. Mustache • Use on the client or server side • Makes generating html simple • Logic-less http://mustache.github.com/ http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-using-the-mustache-template-library/
  • 59. Your template var commentTemplate = '{{#results}}<li class="feed-item-small clearfix last" id="comment_{{id}}_{{type}}"> <a href="{{url}}" class="mem-photo-small" title="{{name}}"><img src="{{photo}}" alt="{{name}}" /></a> <div class="feed-item-content-small"> <a href="{{url}}" title="{{name}}">{{name}}</a> <p>{{#dontescape}}{{{comment}}}{{/dontescape}}{{^dontescape}}{{comment}}{{/dontescape}}</p> <div class="feed-item-action D_empty D_less"> Posted {{posted}} <span id="likewidget_{{id}}" class="commentCountBadge">{{{likes}}}</ span> {{#ismember}}{{{liked}}} {{#iscomment}}| <a href="#" class="deleter" title="Delete this comment" id="delete_{{id}}_{{type}}">Delete</a>{{/iscomment}}{{/ismember}} </div> </div> </li>{{/results}}';
  • 60. Your data var view = { results: { name: item.member_name, url: Chapter.groupUrl + "members/" + item.member_id, photo: item.photo_url, comment: item.comment_text, id: item.id, table: item.table_name, posted: item.date, likes: function() { var length = item.likes.length; return (length > 0 ? '| <a href="#" class="likedialog" data-type="' + item.table_name + '" id="likedialog_' + item.id + '"><span class="bold">' + length + ' likes</span></a> |' : '|'); }, liked: function() { var ids = $.map(item.likes, function(id) { return id.member_id; }); return $.inArray(Member.id, ids) > -1 ? '<a href="#" class="cvoter" data-type="' + item.table_name + '" title="Unlike this comment" id="cvoter_' + item.id + '">Unlike</a>' : '<a href="#" class="cvoter" data-type="' + item.table_name + '" title="Like this comment" id="cvoter_' + item.id + '">Like</a>'; }, type: item.is_suggestion_comment ? "idea" : "event", ismember: Member.isMember, iscomment: Member.id === item.member_id && item.table_name != 'event_diff', dontescape: item.table_name == 'event_diff' } };
  • 61. Put it together var comment = $.mustache(commentTemplate, view); You now have generated html you can insert into the DOM
  • 62. Tip: Use event delegation with templates or don’t forget to unbind! $(“#myid”).delegate(“.someclass”,”click”,function{...});
  • 64. Cache variables Don’t re-evaluate throughout the code var savedGuestCount = 0, savedPolicyState = false, suggestParamValue = null, //'date' or 'venue' isLoaded = false, hours12 = ["7", "8", "9", "10", "11", "12", "1", "2", "3", "4", "5", "6"], hours24 = ["19", "20", "21", "22", "23", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18"], minutes = ["00", "15", "30", "45"], evtdets = $("#eventdets"), templates = Meetup.Templates,.....
  • 65. Add context to selectors and cache them Context $("a.clickme").addClass("red"); //Not bad $("#container").find("a.clickme").addClass("red"); //Better $("#container a.clickme").addClass("red"); //Better Cache Selectors var clickmes = $("#container").find("a.clickme"); clickmes.addClass("red");
  • 66. So what’s the outcome?
  • 67. Waterfall (November 2009) Thats a big waterfall 4.643s
  • 68. Waterfall (January 2011) Thats better 3.826s 18% decrease in load time
  • 69. Read! • JavaScript Performance Rocks! • High Performance Web Sites • Even Faster Web Sites • JavaScript Patterns
  • 70. Thanks Email: justin@meetup.com Twitter: @jcataldo LinkedIn: linkedin.com/in/jcataldo

Notas del editor

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n
  48. \n
  49. \n
  50. \n
  51. \n
  52. \n
  53. \n
  54. \n
  55. \n
  56. \n
  57. \n
  58. \n
  59. \n
  60. \n
  61. \n
  62. \n
  63. \n
  64. \n
  65. \n