#NewMeetup Performance

792 views

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
792
On SlideShare
0
From Embeds
0
Number of Embeds
25
Actions
Shares
0
Downloads
4
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • #NewMeetup Performance

    1. 1. Meetup Performance Justin Cataldo, Lead UI Engineer (@jcataldo)
    2. 2. Why do we care?
    3. 3. Why do we care? Slow Site
    4. 4. Why do we care? Slow Site Bad User Experience
    5. 5. Why do we care? Slow Site Bad User Experience Drop in Member Activity (Less people meeting up)
    6. 6. So what should we do?
    7. 7. Reduce as much as possible!
    8. 8. But what are we reducing?
    9. 9. But what are we reducing? • JavaScript requests
    10. 10. But what are we reducing? • JavaScript requests • Image requests
    11. 11. But what are we reducing? • JavaScript requests • Image requests • DOM
    12. 12. But what are we reducing? • JavaScript requests • Image requests • DOM • CSS
    13. 13. Reducing JavaScript• Externalize• Concatenate• Load only what you need upfront• Position accordingly
    14. 14. Blast from the past (November 2009)
    15. 15. Pre JS Reduction (November 2009)
    16. 16. Why externalize?• Inline scripts block• Lose the benefit of caching• Code reusability
    17. 17. Concatenate• Sprockets (www.getsprockets.com)• Ruby library that preprocesses and concatenates JavaScript files• Baked into our build process
    18. 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. 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>
    20. 20. One gotcha
    21. 21. Caching Split your files up!You will lose the benefit of caching if constantly changing a large file
    22. 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. 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. 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. 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. 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">Nomembers have been marked attended</trn:message>"; Meetup.Copy.noMembersMarkedAttendedDynam = <trn:messagekey="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">Nomembers have been marked absent</trn:message>";</web:script>
    27. 27. If you can’t move to the bottom, lazy load
    28. 28. Post JS Reduction (November 2009)
    29. 29. Post JS Reduction (January 2011)
    30. 30. Defer imagesLazy loading for images
    31. 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. 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. 33. Embed images (If you can)• Use Data-uri/MHTML• Embeds the images in your code• Reduces requests • but increases file size
    34. 34. Use vector images (If you can)• Supported in all browsers• raphael.js makes it easy• Uses SVG and VML
    35. 35. Oh and, Smush those images!
    36. 36. Now tame that DOM
    37. 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
    38. 38. http://mir.aculo.us/dom-monster/
    39. 39. DOM Monster“DOM Monster is a cross-platform, cross-browser bookmarklet that will analyze theDOM & other features of the page youre on,and give you its bill of health.”
    40. 40. We have a case of divitis and DOM Monster is the cure
    41. 41. Clean up your CSS• Remove unused CSS• Use efficient selectors• Reduce CSS
    42. 42. 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
    43. 43. Use PageSpeed http://code.google.com/speed/page-speed/
    44. 44. Reducing is a start, so now what?
    45. 45. Profile your code Every ms count
    46. 46. Tools for profiling• Firebug - Firefox• DynaTrace AJAX Edition - IE• Web Inspector - Chrome/Safari
    47. 47. Speed up that JavaScript
    48. 48. Let’s optimize thisfunction 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); };};
    49. 49. Time itfunction 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”);};
    50. 50. Time itfunction 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
    51. 51. Optimizefunction 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”);};
    52. 52. Optimizefunction 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
    53. 53. 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”);};
    54. 54. 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”);};
    55. 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”);}; 2ms
    56. 56. Use a templating framework
    57. 57. 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/
    58. 58. Your templatevar 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}};
    59. 59. Your datavar 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 }};
    60. 60. Put it togethervar comment = $.mustache(commentTemplate, view); You now have generated html you can insert into the DOM
    61. 61. Tip: Use event delegation with templates or don’t forget to unbind! $(“#myid”).delegate(“.someclass”,”click”,function{...});
    62. 62. Quick tips
    63. 63. Cache variablesDon’t re-evaluate throughout the codevar 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,.....
    64. 64. Add context to selectors and cache themContext$("a.clickme").addClass("red"); //Not bad$("#container").find("a.clickme").addClass("red"); //Better$("#container a.clickme").addClass("red"); //BetterCache Selectorsvar clickmes = $("#container").find("a.clickme");clickmes.addClass("red");
    65. 65. So what’s the outcome?
    66. 66. Waterfall(November 2009) Thats a big waterfall 4.643s
    67. 67. Waterfall(January 2011) Thats better 3.826s 18% decrease in load time
    68. 68. Read!• JavaScript Performance Rocks!• High Performance Web Sites• Even Faster Web Sites• JavaScript Patterns
    69. 69. ThanksEmail: justin@meetup.comTwitter: @jcataldoLinkedIn: linkedin.com/in/jcataldo

    ×