While I was creating Conquer-on-Contact I was annoyed to find out the game's initial load times were very high. Some of this is just because App Engine's performance is damn inconsistent from minute-to-minute, but a look at Firebug's Net tab revealed something shocking:
Oh my word Firefox, what are you doing? Serial downloading in 2009? Here is the offending code:
<script type="text/javascript" src="/js/jquery.js"></script> <script type="text/javascript" src="/js_min/jquery_form.js"></script> <script type="text/javascript" src="/js_min/jquery_countDown.js"></script> <script type="text/javascript" src="/js_min/jquery_color.js"></script> <script type="text/javascript" src="/js_min/jquery_pulse.js"></script> <script type="text/javascript" src="/js_/conquer-on-contact.js"></script>
After some Googling I found that IE8 loads scripts in parallel automatically but for some reason Firefox 3.0 is still in the dark ages. This MSDN blog entry is helpful, but I don't really want a hacky client-side solution.
What's the best way to solve this, then? Well, we could combine all the javascript files manually, but this makes them a pain to edit. I don't need to be messing around with jquery.js when I am just trying to use the library.
So why not keep them as separate files but combine them server-side when serving? This will waste some cpu but we can minimize that by caching the results for at least an hour. I set it up starting with some automatic javascript minification code written by Austin Chau. Add something like this to Austin's javascript.py (but change the filenames to fit your own javascripts!):
class JsAgg(webapp.RequestHandler):
def get(self):
data = memcache.get(key="JSAGG")
if data is None:
filenames = ["js/jquery.js",
"js/jquery_form.js",
"js/jquery_countDown.js",
"js/jquery_color.js",
"js/jquery_pulse.js",
"js/conquer-on-contact.js"]
data = ""
for filename in filenames:
data += minify(filename) + '\n'
memcache.add(key="JSAGG", value=data, time=memCacheExpire)
self.response.headers['Content-Type'] = 'text/javascript'
self.response.out.write(data)
...
# serving minified/aggregated javascript from mem cache
apps_binding.append(('/js_agg/', JsAgg))
[Download the modified javascript.py]
Now change the JavaScript loader in your html:
<script type="text/javascript" src="/js_agg/"></script>
So what does the page load profile look like now?
Of course we are getting screwed by Google Analytics now but you can see the critical change: We went from about 1.1s to load all the Javascript down to about 250ms.
It might be a good idea to add the other handlers (serve aggregated from disk, don't minify, etc) as well but I leave that as an exercise for you.
Some have suggested it would be better to access JQuery via Google's dedicated CDN. I agree this is a better choice, but only once all the major browsers get their act together as far as script loading is concerned.

5 comments: