Javascript
Developers, Please Stop Fucking With Your JavaScript Files
There have been waaaaaaayyyyyy too many posts lately on "supercharging" your JavaScript (and CSS) and creating "dynamic" JavaScript. Bad. Bad. Bad Developers. For an example of what I'm talking about take a look at this article (as posted on DZone), which got 18 up diggs and is described as "Best practices for Javascript delivery..." Ugh. I could write a "Best Practices For Taking A Dump" and it would come out smelling better then that post. Let's break it down...
Client VS Server Side
I don't mean to focus on the supercharging article (which, unfortunately, also has a CSS version), there are a bunch of others too (although most don't stretch on for 7(!) pages). Like this one, which caused me to lock myself in my office for 3 hours. What happened during that time is buried so deep in my mind I'd need years of hypnotherapy to even scratch the surface.
The supercharging post opens with:
I am an unapologetic stickler for speed when it comes to Web applications.
Then he proceeds to show a method that effectively speeds up the client side of the app by slowing down the server side. The net result: probably about the same as before.
Yslow Is To Blame
Yahoo's Yslow plugin is to blame for a lot of this. It's provides nice, pretty grades and dares you to do better. It even tells you where the problems are and how to fix them. What's the easiest way to do it? Route all your JS files through your server side language! No, no, no. Wrong. You see Yslow is only measuring front-end performance. It doesn't give a shit how long something takes to generate on the server. To test that you should be using tools like ab or Siege.
Why This Matters
Your webserver, even Apache, can serve static .js and .css files a fuck ton faster then dynamic pages. This is the point where you'll counter with: "Fine. It's true I'm wasting some server resources, but everything will be cached on the client, so FU, it doesn't really matter!" Go ahead and read this article from YUI blog. Or I'll just give you the conclusion:
Keep in mind the empty cache user experience. It might be more prevalent than you think!
How Do I Know If I'm Doing It Wrong?
1) You have something in your webserver's configuration that routes .js or .css files to your server side language, like this:
RewriteRule \.js$ lib/phpsprocket.php [QSA]
2) You direct your javascript/css includes right to your programming language in your HTML, like this:
<script src="/javascript.php"></script>
Making It Fast The Right Way
Make as Few HTTP Requests as Possible
It's a good thing to merge your static files into as few as possible (notice I didn't say one - more on the later). There are a bunch of different ways to do this. Most commonly it should be part of your build routine. This way the generated file can still be a static one and will be served up fast. Heck, you could even go crazy and deploy it to a CDN.
Another way would be to have it process automatically the first time any user hits a page, as long as the results are stored as a static file for the next user. If you use a framework there might already by a library that does this for you.
GZip Everything
Something that can and should be done by your webserver.
Minify Everything
This should be part of the whatever process you have that combines your files.
Caching on the Server
Any static files that are cached on the server should be cached as plain .js or .css in a directory that is web accessible. If you need to use any type of programming to read the cached file you are doing it wrong.
Caching on the Client
As part of the build routine, when you generate the minified and combined files, include either a timestamp or a version number (preferably) as part of the file name. If you use a version number, then your HTML page (which is generated dynamically) can have access to that same number and can include it as part of the file name when it generates the tags to include your static files.
Dynamic JavaScript
I know what your thinking: "But, I need to pipe my javascript through the server side language so that I can generate custom code based on the user."
No. No you don't. Do you think any site with more then three visitors a month does this? Digg? Amazon? Twitter? You will pry my web developer badge from my cold, dead, bullet ridden body before you convince me otherwise.
The needs of the many outweigh the needs of the few...
...or one. Take a look at this simple scenario: You have a JavaScript heavy website that uses jQuery and parts of the jQuery UI on every page. Say you minify and combine those and they become one 100k file. You also have page specific JavaScript files that are around 30k each. If you were hell bent on only including one JavaScript file per page you'd end up with something like:
Page 1: file1.js (130k)
Page 2: file2.js (130K)
What if instead you left the files separate:
Page 1: jquery.js (100k) + file1.js (30k)
Page 2: file2.js (30k)
What about jquery.js on Page 2? It would already have been loaded and cached by the browser on Page 1. And if you are using the trick of including the version as part of the file name and setting a far future expires date, the browser won't even make a request for the file.
All I'm saying here is be smart. It makes more sense to logically group your files and then include the groups you need for a particular page, rather then having one giant file that gets downloaded on every page because 25% of it is different then the other pages.
Wrapping It Up
Please developers, just leave your JavaScript and CSS files as static files. It's cool if you want to mess with them a bit, but make sure the final output is still a plain static file.
Update To Asset Helper
I made a few minor changes to the CakePHP Asset Helper to make it compatible with RC2. The changes were:
- Replaced deprecated vendor() with App::import
- Replaced deprecated mkdirr with mkdir
You can grab the latest version here.
What Is It
The CakePHP Asset Helper automatically combines and packs CSS and JS files, which helps improve the loading of websites.
Update to Asset Packer CakePHP Helper
I just posted a bug fix for my CakePHP Asset Packer. The previous version didn't handle $javascript->codeBlock, when the inline option was set to false. The JavaScript would just disappear...ooops. Updated code is available in the Bakery article or you can download here.
Benchmarking My CakePHP Asset Packer Helper
R. Rajesh Jeba Anbiah left a comment concerned about the performance hit when using the CakePHP Asset Packer Helper.
The major reason for choosing the Asset Helper is to speed up the site. But, the hit on the helper call is heavy–at least for me. [snip] ...for the real benefit, one seems need to manually minify/pack.
I decided to put this to the test. I grabbed a fresh download of the latest CakePHP 1.2-beta and made a basic TestsController with one action with a view. It didn't use a model. I took the default layout and moved it to my /app/views/layouts. In the layout I added 2 Stylesheets and 2 JavaScripts. In the view I included one of each using the $scripts_for_layout method. I used ab, with the parameters "-c 10 -t 60", to do the testing. Since this was all on my local machine network speed isn't really a factor. Essentially I would be getting the times to render the page.
The first test I ran was without the Asset Packer Helper - what you would get with the base CakePHP install.
Here's the baseline results:
Complete requests: 622
Requests per second: 10.36 [#/sec] (mean)
The hope is that including the helper won't hurt these numbers too much. The speed increase gained by using the helper is seen in the download of merged js and css files and since ab is only testing the html file (not the images, css, jss linked within), the benefit of the helper won't be evident in this test.
First I included the helper and commented out the filecaching. This is essentially what happens when the helper is being used for the first time on a particular group of css/js files. Either because one of the files has changed or because the cache hasn't been created yet. Consider this to be the worst case scenario and probably would only really happen a tiny percentage of the time, like right after you do an update. I expected the numbers to take a hit, but not to be this bad:
Complete requests: 32
Requests per second: 0.52 [#/sec] (mean)
Did I emphasize that this scenario doesn't happen very often? I could try to improve this, but I'm not sure it's worth it. This doesn't happen too ofter, plus most of the time is probably in the css and js minifying/packing, which are vendor classes.
Let's take a look at a more common use case. The cached files already exist, so the helper checks to see if there are any changes, finds none and doesn't have to do any extra processing.
Complete requests: 583
Requests per second: 9.69 [#/sec] (mean)
Much better. Still a hit from the baseline numbers, but not too bad.
Here's one more test. In the AssetHelper class there is a var $checkTS. Setting this to false will disable the functionality described above, where the helper will see if any of the original css/js have changed and the cached version needs to be rebuilt. You can safely set this to false if you clean out your packed directories with new builds.
Complete requests: 602
Requests per second: 10.03 [#/sec] (mean)
That's an improvement over the default settings for the helper and are pretty close to the baseline results. The difference is 3.15700 milliseconds/request or .003157 in seconds.
I may look into trying to get that last scenario closer to the baseline, but the difference isn't so significant that I would really worry about it.
So what does the extra .003157 seconds of processing time get you? This isn't a perfect test, but I ran Yslow on the baseline setup and again with the helper. The baseline scored a D(61). With the helper that jumped to a B(81).
Javascript Code Highlighter: Chili vs SyntaxHighlighter
One of my goals with this blog is to post more code snippets. It is something I did occasionally on my old blog. It's one of those things that should be simple, but ends up being a bit of a pain. I've used a Wordpress plugin in the past, but I was never fully satisfied with it. Lately I've seen some other programming blogs I read using a client side Javascript solution. After some digging the two "ready for prime time" packages I found were Chili and SyntaxHighlighter.
This is a comparison of those two. If you feel I missed another package let me know in the comments.
Chili
License: MIT
Includes: jQuery(21KB), core (7KB), PHP CSS(1KB), PHP JS (37KB)
One of the things I initially liked about this package was that it used jQuery, which is my new favorite Javascript library, although then I realized, as much as I use jQuery on my web apps, I don't really have a need for it on this blog. So having to include it was just additional baggage.
I'm not crazy about the overall size. The PHP "recipe" is 37KB alone. Much bigger than any of the others. Another drawback is the weak documentation. The Chili page is long, dominated by the change log - which is good information, but placed poorly. A simple "how to use Chili" section with code samples - USING THE FREAKING PACKAGE would be nice [Update July 24, 2007 - Chili has a new quickstart page. I'll leave me rant here for entertainment purposes].
You know, something like:
1) Include jQuery and the Chili Javascript files. Your paths may be different.
<script type="text/javascript" src="/js/jquery.js"></script>
<script type="text/JavaScript" src="/js/chili-1.8b.pack.js"></script>
2) Chili can automatically find the recipes it needs - simply tell it where to look.
<script id="setup" type="text/javascript">
ChiliBook.automatic = true;
ChiliBook.recipeFolder = "chili/js/";
ChiliBook.stylesheetFolder = "chili/css/";
</script>
3) Wrap your code and tell Chili which recipe to use. Remember to replace & with & < with < and > with >
<pre><code class="php">
<?php
class family_friendly() {
function censor($word) {
return '*' . gettext($word) . '*';
}
}
function get_license() {
return 'Creative Commons';
}
printf('This guide is released under the %s license.<br />', get_license());
printf('Seriously, Chili author, if you are reading this, move 95% of the %s onto secondary pages and include this simple tutorial.<br />', family_friendly::censor("crap"));
?>
</code></pre>
That's all you need. Thats all the information 99% of your users need. I shouldn't have to figure out which of the five example pages I need to view source on to get this working. Especially when they have such confusing names as: examples-all-combinations.html (which appears to be a joke, that I don't get), examples-dynamic-automatic-adhoc.html, examples-dynamic-automatic-adhoc-metaobjects.html, examples-static-automatic-adhoc.html. Huh?
I'm done with my rant. I'll say some nice things about Chili now. One of the features that is really cool is that it's completely automated. You simply wrap your code with the correct tag and Chili does the rest. Also since it is a plugin for jQuery it degrades nicely, leaving the code less pretty, but still completely readable.
Now that I said something nice I can go back to ranting. The samples provided with the download have Google Ads in them. I can understand having them on the actual site, but in the downloaded samples it seems a bit tacky to me. Even worse the ads are all food related, that is when they're actually in English. I assume the author simply took the pages directly from the site and rolled them into the download, but I still find it annoying.
Also the author makes the claim "You just put the code you want highlighted in a code tag AS IS". Then not even 300px later he states "Always escape the content of the code element for HTML: at least substitute any & by & and any < by < in this order. If you want to highlight HTML you also need to escape any > with >." So it isn't exactly "AS IS".
SyntaxHighlighter
License: GNU
Includes:CSS(4KB), core(11KB), PHP (5KB)
SyntaxHighlighter is obviously a much lighter solution. It too has separate includes for each language, called brushes here. The instruction page is also well done. In addition to highlighting and formatting the code SyntaxHighlighter adds buttons that allow the user to view plain, copy to clipboard and print. On the downside it isn't automated like Chili, although you could certainly use jQuery, or some custom javascript to execute HighlightAll on a page load. Like Chile you need to encode the < character (but not > or &).
1) Include the core files and the brushes for any language you want. Your paths may be different.
<script type="text/javascript" src="/syntaxhighlighter/js/shCore.js"></script>
<script type="text/javascript" src="/syntaxhighlighter/js/shBrushPhp.js"></script>
<script type="text/javascript" src="/syntaxhighlighter/js/shBrushJScript.js"></script>
<script type="text/javascript" src="/syntaxhighlighter/js/shBrushXml.js"></script>
2) Put this at the end of your file. If you put it before the code you want highlighted it won't work.
<script class="javascript">
dp.SyntaxHighlighter.ClipboardSwf = "syntaxhighlighter/js/clipboard.swf";
dp.SyntaxHighlighter.HighlightAll("code");
</script>
3) Wrap your code and tell SyntaxHighlighter which brush to use. Remember to replace < with <
<pre name="code" class="php">
<?php
class Highlighters() {
function result($highlighter) {
switch ($highlighter) {
case "chili":
default:
return "FTL";
case "syntaxhighlighter":
return "FTW";
}
}
}
$highlighters = new Highlighters();
printf("Chili is a nice effort, but just not for me. Chili %s", $highlighters->result("chili"));
printf("I am going to give SyntaxHighlighter a shot. SyntaxHighlighter %s", $highlighters->result("syntaxhighlighter"));
?>
</pre>
Wrap Up
If you're already using jQuery and want to highlight non-PHP then Chili is worth checking out. For this blog I'm going to give SyntaxHighlighter a shot. I think end result of SyntaxHighlighter is nicer looking and the additional buttons are useful features.
If you have any experience with either feel free to write about it in the comments.
