Thursday, February 14, 2013

Chris Love on Web Performance optimization for Modern Web Applications

<link rel="shortcut-icon" href="/favicon.ico" />

...is a very important tag! It is not a tag to just not include. Instagram made the mistake of not implementing this tag and ended up having a horror story because of it. If you do not have a favicon, a browser will look for it nonetheless and this will slow down your site even more when it is being hammered with hits. Instagram alleviated the majority of its latency pains during one struggle by just adding this tag to its HTML. This is one of many helpful optimizations that Chris Love suggested in a talk on web optimizations on Saturday at the Dallas Day of Dot Net convention. Other tidbits:

  • Optimizing for the web helps SEO.
  • Minimize 3rd party scripts. It is OK to have Google analytics by itself perhaps, but not JavaScript for five different analytics packages. The marketers that think this helps you do not realize that it in fact hurts you by slowing down your site and thus discouraging visitors.
  • Local storage adds less weight than cookies. It is quicker.
  • Image sprites are cool.

I will end with offering that Chris heavily encouraged that JavaScript not be kept in blobs within HTML itself. It should be pulled out to .js files. Blocks of code in HTML load as the page loads and ultimately slow down the loading of a page. The .js files load asynchronously as needed. We talked about how to get around the problem of an inability for variables from C# to bubble up into JavaScript without having JavaScript directly in a view or web form as clearly C# cannot be injected into a .js file. Chris suggested that the dynamic content could populate hidden form fields from which it might be grabbed by JavaScript in a .js file. Here is an example of the sort of clean up we discussed. Below is an MVC view from the code I wrote for this:

<h4>AJAJ-style "AJAX" in progressive record loading:</h4>
<div id="records">
   <div class='altout'>
      <div class='in'>License Number</div>
      <div class='in'>Expiration Date</div>
      <div class='in'>Must Wear Glasses?</div>
   </div>
</div>
@Html.Partial("Next", "Three")
<script type="text/javascript">
   $(function () {
      var collection = new Array();
      collection.length = @ViewBag.OneThirdOfRecordCount;
      var counter = 0;
      $(collection).each(function () {
         $('#records').animate({ opacity: "1" }, 100, function () {
            $.ajax({
               type: "POST",
               url: '/Ajax/GetPageOfRegisteredDriverRecordSet/' + counter,
               dataType: 'json',
               success: function (result) {
                  $.each(result, function(index, value) {
                     var html = "<div class='out'>";
                     html = html + "<div class='in'>" + value.Id + "</div>";
                     html = html + "<div class='in'>" + value.Date + "</div>";
                     html = html + "<div class='in'>" + value.Bool + "</div>";
                     html = html + "</div>";
                     $('#records').append(html);
                  });
                }
            });
            counter = counter + 3;
         });
      });
   });
</script>

 
 

I refactored it to be like so:

<h4>AJAJ-style "AJAX" in progressive record loading:</h4>
<div id="records">
   <div class='altout'>
      <div class='in'>License Number</div>
      <div class='in'>Expiration Date</div>
      <div class='in'>Must Wear Glasses?</div>
   </div>
</div>
<input type="hidden" value="@ViewBag.OneThirdOfRecordCount" id="hidden"/>
@Html.Partial("Next", "Three")
<script src="@Url.Content("~/Scripts/progressivelyloadrecords.js")"
      type="text/javascript"></script>

 
 

progressivelyloadrecords.js is a new file containing:

$(function () {
   var collection = new Array();
   collection.length = $('#hidden').val();
   var counter = 0;
   $(collection).each(function () {
      $('#records').animate({ opacity: "1" }, 100, function () {
         $.ajax({
            type: "POST",
            url: '/Ajax/GetPageOfRegisteredDriverRecordSet/' + counter,
            dataType: 'json',
            success: function (result) {
               $.each(result, function (index, value) {
                  var html = "<div class='out'>";
                  html = html + "<div class='in'>" + value.Id + "</div>";
                  html = html + "<div class='in'>" + value.Date + "</div>";
                  html = html + "<div class='in'>" + value.Bool + "</div>";
                  html = html + "</div>";
                  $('#records').append(html);
               });
            }
         });
         counter = counter + 3;
      });
   });
});

 
 

I was pleasantly surprised I didn't have to use parseInt() on the value I grabbed out of the hidden form field.

No comments:

Post a Comment