<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<title>Optimizing Perceived Performance</title>
<link rel="stylesheet" href="stylesheets/screen.css" type="text/css" media="screen" charset="utf-8">
<link rel="stylesheet" href="stylesheets/prettify.css" type="text/css" media="screen" charset="utf-8">
<script src="javascripts/jquery-1.3.2.min.js" type="text/javascript" charset="utf-8"></script>
<script src="javascripts/prettify.js" type="text/javascript"></script>
<script src="javascripts/jquery.jquinote.js" type="text/javascript" charset="utf-8"></script>
<script src="javascripts/opp.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<h1>Optimizing Perceived Performance</h1>
<div id="slide-container">
<ol id="slides">
<li class="centered">
<h2><span>Testing</span></h2>
</li>
<li />
<li>
<h2><span>Optimizing Perceived Performance</span></h2>
<img src="/images/ricer.jpg" />
</li>
<li>
<h2><span>About Me</span></h2>
<img src="/images/dce.jpg" class="top-pad">
</li>
<li class="centered">
<h2><span>My Startup</span></h2>
<h3>“DBDB”</h3>
<span class="url">http://dbdb.local/home</span>
</li>
<li>
<h2><span>The Problem</span></h2>
<img src="/images/bagfactor.jpg" class="top-pad"/>
</li>
<li>
<h2><span>The Problem (Con’t)</span></h2>
<div class="code">
<pre class="prettyprint big">
def bagfactor
sleep(2)
rand(50) / 10.0
end
</pre>
</div>
</li>
<li>
<h2><span>http://github.com/dce/dbdb</span></h2>
<ul>
<li>Rails app</lidoesn’t>
<li>public/slides.html</li>
<li>public/javacripts/jquery.jquinote.js</li>
</ul>
</li>
<li>
<h2><span>It could happen to you</span></h2>
<img src="/images/encino.jpg" class="top-pad" />
</li>
<li>
<h2><span>First Impressions</span></h2>
<img src="/images/handshake.jpg" />
</li>
<li>
<table>
<tr>
<th>Up to...</th>
<th>User response</th>
</tr>
<tr>
<td>0.1 second</td>
<td>instantaneous</td>
</tr>
<tr>
<td>1 second</td>
<td>responsive</td>
</tr>
<tr>
<td>10 seconds</td>
<td>slow</td>
</tr>
<tr>
<td>> 10 seconds</td>
<td>gone</td>
</tr>
</table>
<p class="citation">– About Face 3</p>
<span class="url">http://dbdb.local/</span>
</li>
<li class="centered">
<h2><span>Bring in the AJAX</span></h2>
</li>
<li>
<h2><span>Progressive Enhancement</span></h2>
<img src="/images/evolution.jpg" class="top-pad" />
</li>
<li>
<h2><span>Unobtrusive Javascript</span></h2>
<img src="/images/gob.jpg" class="top-pad" />
</li>
<li>
<h2><span>HIJAX</span></h2>
<img src="/images/conair.jpg" class="top-pad" />
</li>
<li>
<h2><span>jQuery</span></h2>
<img src="/images/awesome.jpg" />
</li>
<li>
<div class="code">
<pre class="prettyprint">
class DbsController < ApplicationController
def show
@db = Db.find(params[:id])
respond_to do |format|
format.html
format.js do
render :partial => "profile",
:locals => { :db => @db }
end
end
end
end
</pre>
</div>
</li>
<li>
<div class="code executable" title="HIJAX">
<pre class="prettyprint">
$(".db-list a").click(function() {
link = $(this);
$.ajax({url: $(this).attr("href"),
success: function(src) {
link.parents("dt").after("<dd>" + src + "</dd>");
link.unbind('click').click(function() {
$(this).parents("dt").next("dd").toggle();
return false;
})
}
});
return false;
});
</pre>
</div>
<span class="url">http://dbdb.local/</span>
</li>
<li>
<h2><span>Make It Snappy</span></h2>
<img src="/images/ohsnap.jpg" class="top-pad" />
</li>
<li>
<div class="code executable" title="More Responsive">
<pre class="prettyprint">
$(".db-list a").click(function() {
link = $(this);
link.parents("dt").after('<dd class="spinner"></dd>')
.next("dd").hide().slideDown("slow");
link.unbind("click").click(function() {
$(this).parents("dt").next("dd").slideToggle();
return false;
});
$.ajax({url: link.attr("href"),
success: function(src) {
link.parents("dt").next("dd").html(src)
.removeClass("spinner");
}
});
return false;
});
</pre>
</div>
<span class="url">http://dbdb.local/</span>
</li>
<li>
<h2><span>Take Advantage of Downtime</span></h2>
<img src="/images/gmail.jpg" />
</li>
<li>
<div class="code executable" title="Background Loading">
<pre class="prettyprint">
jQuery.fn.loadContentInOrder = function() {
return this.each(function() {
link = $(this);
$.ajax({url: link.attr("href"),
success: function(src) {
display = link.parents("dt").next("dd").html(src)
.removeClass("spinner")
.next("dt").find("a").loadContentInOrder();
}
});
});
};
$(".db-list a").each(function() {
$(this).parents("dt").after("<dd class=\"spinner\"></dd>")
.next("dd").hide();
});
$(".db-list a").click(function() {
$(this).parents("dt").next("dd").slideToggle();
return false;
});
$(".db-list a:first").loadContentInOrder();
</pre>
</div>
<span class="url">http://dbdb.local/</span>
</li>
<li>
<h2><span>Isolate Bottlenecks</span></h2>
<img src="/images/github.jpg" />
</li>
<li>
<h2><span>JSON</span></h2>
<div class="code">
<pre class="prettyprint">
{ "db": {
"id": 13,
"name": "Tyler Hansbrough",
"occupation": "UNC Basketball Player",
"bagfactor": 1.3,
"avatar_id": 61
}
}
</pre>
</div>
</li>
<li>
<div class="code">
<pre class="prettyprint">
class DbsController < ApplicationController
def show
@db = Db.find(params[:id])
respond_to do |format|
format.html
format.js { ... }
format.json do
render :json => { :bagfactor => @db.bagfactor }
end
end
end
end
</pre>
</div>
</li>
<li>
<div class="code executable" title="Isolate Bottlenecks">
<pre class="prettyprint">
jQuery.fn.loadBagfactor = function() {
img = $(this);
$.ajax({url: this.parents("dd").prev("dt")
.find("a").attr("href"),
data: { format: "json" }, dataType: "json",
success: function(db) {
img.replaceWith(db.bagfactor);
$("img.spinner:first").loadBagfactor();
}
});
};
$("img.spinner:first").loadBagfactor();
</pre>
</div>
<span class="url">http://dbdb.local/v4</span>
</li>
<li>
<h2><span>Worst Best Solution</span></h2>
<ul>
<li>Most information in least time</li>
<li>Poor degredation</li>
<li>Optimize for users' needs</li>
</ul>
</li>
<li class="centered">
<h2><span>New Problem</span></h2>
</li>
<li>
<img src="/images/pythonistas.jpg" />
<span class="url">http://dbdb.local/dbs/new</span>
</li>
<li>
<h2><span>Remove Blocking Operations</span></h2>
<img src="/images/mclovin.jpg" class="top-pad"/>
</li>
<li>
<h2><span>The Usual Way</span></h2>
<ul>
<li>Hidden iFrame</li>
<li>Second form w/ iFrame as target</li>
<li>Server sends back JS to update page</li>
</ul>
</li>
<li>
<h2><span>Something Sorta Nuts</span></h2>
<img src="/images/squirrel.jpg" />
</li>
<li>
<h2><span>avatars/new.html.erb</span></h2>
<div class="code">
<pre class="prettyprint">
<% form_for @avatar, :html => { :multipart => true } do |f| %>
<%= f.file_field :image %>
<% end %>
<% javascript_tag do %>
$("input").change(function() {
$(this).hide().after('<%= image_tag "spinner.gif" %>');
$(this).parents('form').submit();
});
<% end %>
</pre>
</div>
</li>
<li>
<h2><span>avatars/create.html.erb</span></h2>
<div class="code">
<pre class="prettyprint">
<%= image_tag @avatar.image.url(:thumb) %>
<% javascript_tag do %>
$("form", top.document).append(
'<%= hidden_field_tag "db[avatar_id]", @avatar.id %>');
<% end %>
</pre>
</div>
</li>
<li>
<div class="code executable" title="Non-Blocking Upload">
<pre class="prettyprint">
$("input[type=file]").replaceWith(
'<iframe src="/avatars/new"></iframe>');
</pre>
</div>
<span class="url">http://dbdb.local/dbs/new</span>
</li>
<li>
<h2><span>Cool? Lame?</span></h2>
<img src="/images/wyclef.jpg" class="top-pad">
<p class="caption">(It doesn't matter)</p>
</li>
<li class="centered">
<h2><span>Server Side</span></h2>
</li>
<li>
<blockquote>
“At least 80 percent of the time it takes to display a web page happens
after the HTML document has been downloaded.”
</blockquote>
<p class="citation">— Steve Souders, High Performance Web Sites</p>
</li>
<li>
<h2><span>Fewer HTTP Requests</span></h2>
<ul>
<li>Combine scripts & CSS files</li>
<li>Use CSS sprites</li>
<li>Avoid redirects when possible</li>
</ul>
</li>
<li>
<h2><span>Optimize Browser Caching</span></h2>
<ul>
<li>Use a far-future <code>expires</code> header</li>
<li>Put JS & CSS in external files</li>
<li>Apache mod_expires</li>
</ul>
</li>
<li>
<h2><span>Reduce File Size</span></h2>
<ul>
<li>Minify JS - asset_packager</li>
<li>Use GZIP</li>
<li>Apache mod_deflate</li>
</ul>
</ul>
<li class="centered">
<h2><span>What's Next</span></h2>
<h3>Rails 3</h3>
</li>
<li>
<h2><span>The <code>link_to_remote</code> Helper</span></h2>
<div class="code">
<pre class="prettyprint">
<%= link_to_remote @db.name, :url => db_url(@db),
:method => :get %>
</pre>
</div>
</li>
<li>
<h2><span>The Old Way</span></h2>
<div class="code">
<pre class="prettyprint">
# <%= link_to_remote @db.name, :url => db_url(@db),
# :method => :get %>
<a href="#" onclick="$.ajax({data:'authenticity_token=' +
encodeURIComponent('2b79b50423e099...'),
dataType:'script', type:'get',
url:'http://dbdb.local/dbs/13'}); return false;">
Tyler Hansbrough
</a>
</pre>
</div>
</li>
<li>
<h2><span>The New Way</span></h2>
<div class="code">
<pre class="prettyprint">
# <%= link_to_remote @db.name, :url => db_url(@db),
# :method => :get %>
<a href="/dbs/13" data-remote="true" data-method="get">
Tyler Hansbrough
</a>
</pre>
</div>
</li>
<li>
<h2><span>The New Way (Con’t)</span></h2>
<div class="code">
<pre class="prettyprint">
# <%= link_to_remote @db.name, :url => db_url(@db),
# :method => :get %>
# <a href="/dbs/13" data-remote="true" data-method="get">
# Tyler Hansbrough
# </a>
$("a[data-remote=true]").click(function() {
$.ajax({url: $(this).attr("href"),
method = this.dataset.method,
success: function(src) {
link.parents("dt").after("<dd>" + src + "</dd>");
}
});
});
</pre>
</div>
</li>
<li>
<div class="code">
<pre class="prettyprint">
<a href="/dbs/13" class="remote">
Tyler Hansbrough
</a>
<!--
$("a.remote").click(function() {
$.ajax({url: $(this).attr("href"),
...
});
});
-->
</pre>
</div>
</li>
<li class="centered">
<h2><span>Conclusion</span></h2>
</li>
<li class="centered">
<h2><span>Consider perceived performance</span></h2>
</li>
<li class="centered">
<h2><span>Write your own JS</span></h2>
</li>
<li>
<h2><span>Optimize your server config</span></h2>
<img src="/images/hpws.jpg" />
</li>
<li class="centered">
<h2><span>Thank You</span></h2>
<h3>http://speakerrate.com/talks/1182</h3>
</li>
<li class="centered">
<h2><span>Your Turn</span></h2>
<h3>Stories? Questions? </h3>
</li>
</ol>
</div>
</body>
</html>