mum of four parallel connections to
download your content—which means
you cannot afford to rely on multiple
external sources to load in a reasonable amount of time. Finally, the HTTP
1. 1 specification recommends at most
two parallel downloads per hostname.
Of course these values are changing
all the time; to find tools and data on
the latest limits, Steve Souder’s Mobile
Cache File Sizes7 and Ryan Grove’s Mobile Browser Cache Limits, Revisited6
are good starting points.
If you truly want to solve the cache
issue, it is important to force the mobile browser to cache all the unchanging content on a site permanently,
using HTML5 application cache, commonly referred to as app cache. Because app cache is still on the bleeding
edge, it is best to use the minimal subset possible for your application.
The cache is controlled by a single
manifest file that tells the browser
where to get resources. The minimum
functionality would be to make your
website load as a single large Web page,
and have the manifest file list that page
and a hashCode as a comment that is
updated when the Web page changes.
Dynamic content can be loaded via
XHR (XMLHttpRequest) and inserted
into the document. This technique is
most effective if you design the prefix
of your page (up to about the 1,400th
byte, in order to fit into a single packet
across almost any TCP/IP network) to
render a basic framework for the page
before any script is executed.
If you design your site in this way,
you can also have the framework
of the page fade in by providing an
opacity transition from 0.0001 to 1.0
just after the framework of markup
is loaded. This will create a very slick
“app-like” startup experience. The
opacity property is the method of
choice for smooth transitions because
the browser will have prerendered everything that is at opacity 0.0001, but
the user cannot see it, and fading in is
slick and smooth. Using opacity 0 will
cause a white flash when the browser
repaints, and using the display CSS attribute will be even worse because it
will cause re-layout. If you cannot get
the basic framework of your page to fit
into the first packet, then you can instead load a spinner or a logo, again at
opacity 0.0001, and conditionally de-
figure 3. Reduce abandonment with a conditional spinner for non-approached loads.
<script>
if ( window.applicationCache.status == 0) {
// Page was loaded from the Network; reveal the spinner
} else {
// Page was loaded from AppCache; heavier-weight startup applies
}
</script>
tect whether the app cache is already
populated before showing the spinner. The JavaScript for accomplishing
this should appear in a script tag after
the markup, as illustrated in Figure 3.
Using this pattern ensures the user
will immediately get the feedback that
your page is loading, thus reducing the
chance of abandonment at the most
critical moment: when the user first
discovers your site.
To complete the effect, you also
need to ensure your serving infrastructure flushes the framework of the page
to the network before doing any heavy
lifting on the server side, so that all
overhead on first request is deferred
until after the initial packet is sent.
This will prove remarkably speedy even
on spottier connections. It is also not a
bad idea to make a request to the server
at this point to track the page-load metric (more on this later in the section on
JavaScript parse performance).
Following the recommended pattern means that even on the first load,
all code, assets, and CSS for your site
will load in a single round trip, and
that will create the best possible mobile experience. Dynamic data will of
course have to be loaded separately,
and the same level of care and caching should apply there, too, except that
HTML5 storage APIs will have to be
used to cache follow-on content. Fewer than four round trips—one for the
DNS lookup, one for the initial page
content, and up to two for dynamic
content—should produce a responsive
mobile experience for your users.
Page Speed’s best practices for minimizing round-trip times4 also include
a number of techniques for avoiding
round trips you might overlook, such as
DNS lookups and redirects. Almost every recommendation relating to round-trip times is well worth implementing,
as Google has provided good techniques
for combining the content that forms
all the components of your site.
The last item in that section of
Google’s recommendations focuses
on parallelism achieved by serving
from multiple hostnames; but parallel downloads are likely to prove ineffective for mobile devices. Combining your resources and components
into single files and ensuring they are
served from app cache will be the most
effective technique.
Use Compression. Using compression on all content that would benefit
(essentially everything except images
and video) remains a great recommendation for mobile. Mobile CPUs are
getting faster much more quickly than
mobile networks, and their speed does
not change when a user is visiting the
cottage. Even if you have diligently used
app cache and local storage to save all
resources locally on the device, you can
expect the user will be regularly loading
dynamic content, and all these requests
should be compressed. To speed initial
load time, even cached content should
be delivered compressed whenever
possible, though the benefit is less.
Manage JavaScript Parse Time.
After caching and compression, script
loading is likely to be the single biggest
source of performance degradation for
a website, and it is the more difficult to
address. The big issue on mobile is that
parse and execute times of script are
much slower than one would expect. In
fact, the rule of thumb is that parse and
execute times are 10 times slower than
when testing on a desktop. A JavaScript
payload of as little as 100KB can cost
100ms of startup time even on reasonably recent phones such as iPhone 4—
and the previous generation of iPhone
was 10 times slower!
Therefore, it is certainly worth putting the vast bulk of script at the bottom of the page. To evaluate parse
time, you can use two script tags as
shown in Figure 4.
The start checkpoint is parsed and
evaluated before parsing for the sec-