Search This Blog

Thursday, February 26, 2009

How to Easily Combine, Minify, and Cache JavaScript and CSS with 1&1 (And Other Web Hosts)


An Opening Word

In How to Compress Perl/CGI-Scripts... and How to Compress PHP and Other Text-Based Files..., I write about achieving better standards according to Yahoo’s Best Practices for Speeding up Websites guide.

If your server supports PHP5, now there is an incredibly easy way to minify both JavaScript and CSS files on the fly, PLUS a way to combine a number of different files into a single HTTP request. (See Step 4 of my blog entry on compressing PHP scripts to convert your site to PHP5, particularly if you are using a 1&1 shared hosting package.)

Thanks to Ryan Grove and Steve Clay, Minify! is now here (download link). As the source page on GoogleCode suggests, it combines, minifies, and caches JavaScript and CSS files on demand to speed up page loads.

Setup and Basic Use

Just download the zip file and drop the min folder in your root directory.

Then, to include single resources on your page, just set them up like the following with ?f= (f short for "file"), followed by the URL of the resource:
// css
<link rel="stylesheet" href="/min/?f=/path_to_file/file.css" type="text/css" />
// js
<script type="text/javascript" src="/min/?f=/path_to_file/file.js"></script>
You can also use URL-rewriting if your host supports it (see Note below):
// js -- notice the "?" before the "f" has been dropped
<script type="text/javascript" src="/min/f=/path_to_file/file.js"></script>
Minify comes with its own URL-builder for easily creating and checking links which you can access at http://name_of_your_site_here.com/min/index.php once you have it installed on your server.

The builder interface itself will teach you how to serve groups of files as a single, minified resource by adding a few lines to /min/groupsConfig.php, but we'll go ahead and look at it here anyway:
// In /min/groupsConfig.php, I set up the unique identifier "js"
// to specify a group of 5 hypothetical JS files
return array(
    'js' => array(
        '//js/file1.js',
        '//js/file2.js',
        '//js/file3.js',
        '//js/file4.js',
        '//js/file5.js'
    )
);
You can then include this group of files in your HTML tag by specifying g (for "group"), followed by your unique js identifier.
<script type="text/javascript" src="/min/g=js"></script>
Or you can take full advantage of comma-delimited arrays and set up as many sets of files paired with unique identifiers as you want (of course including any CSS files using the link tag, as shown in the first example on this page):
// "js" and "css" file groups set in /min/groupsConfig.php
return array(
    'js' => array(
        '//js/file1.js',
        '//js/file2.js',
        '//js/file3.js',
        '//js/file4.js',
        '//js/file5.js'
    ),
    'css' => array(
        '//css/file1.css',
        '//css/file2.css',
        '//css/file2.css'
    )
);

Setting a Long Cache Life for Your Pages

Minify's default cache life is 30 minutes for CSS and JS files. This works well for development servers, but for production servers with stable code, a longer cache time is recommended. Your can easily implement a year-long cache life in the HTML simply by appending a number (yes, any number) as a query string variable.
// css
<link rel="stylesheet" href="/min/g=css&1" type="text/css" />
// js
<script type="text/javascript" src="/min/g=js&1"></script>
However, you must update this number if you make changes to these files and it is important that users have the latest versions. For this reason, I recommend starting off with the number 1 and incrementing it as your changes dictate, though as long as the numbers are always different, it makes no difference how you do it.

Since this approach requires manual editing, this may not be optimal for you. For the full spectrum of caching possibilities, see the developer doc HttpCaching; for advanced options of specifying a different cache system (as well as other advanced uses) see the CookBook doc.

For more general use ideas, including how to specify a b for "base directory" when a group of single files all share the same root directory path, see the developer README file beginning with the heading "Minifying a Single File"; for advanced usage beyond the scope of the examples here, see the developer comments at CustomSource.

Note: If you want to use rewritten urls and you are using a Linux shared hosting package with 1&1 (and maybe even if you're not), you will probably need to make a small adjustment to the included .htaccess file in the min directory. The line in the native code you're after reads as follows:
RewriteRule ^([a-z]=.*)  index.php?$1  [L,NE]
but you will probably need to change it:
RewriteRule ([a-z]=.*) /min/index.php?$1 [L,NE]
Also to point out the obvious, make sure if you put the min directory a level deeper (see below), you specify the sub-directory: for example, /myscripts/min/index.php.

Note on Use Inside A Sub-Directory

By default, Minify is intended to have the min directory parked at the root level. It works just fine a directory level deeper, however, though this sometimes causes problems with the URL-building interface. The developer docs recommend a solution that uses PHP; see AlternateFileLayouts.

Update: I also ran into a small issue when minifying HTML: see the gray box at the end of How to Minify HTML using PHP and Minify (including final HTML output of PHP files) for the work-around.

If you prefer to hatch a solution to the builder interface in JavaScript, you'll need to modify the code slightly. Use your IDE or text editor to open the _index.js file found in the builder directory.

The first block of code currently reads as follows; our interest here all deal with _minRoot:
var MUB = {
    _uid : 0
    ,_minRoot : '/min/?'
    ,checkRewrite : function () {
        var testUri = location.pathname.replace(/\/[^\/]*$/, '/rewriteTest.js').substr(1);
        function fail() {
            $('#minRewriteFailed')[0].className = 'topNote';
        };
        $.ajax({
            url : '../f=' + testUri + '&' + (new Date()).getTime()
            ,success : function (data) {
                if (data === '1') {
                    MUB._minRoot = '/min/';
                    $('span.minRoot').html('/min/');
                } else
                    fail();                
            }
            ,error : fail
        });
    }
Replace this with the following code, which is not particularly pretty, but is "smart" and will read the location of the directory when the script is called, no matter how many levels deep. The three changes are marked below by the comment // here.
var MUB = {
    _uid : 0
    ,_minRoot : location.pathname.replace('/builder/', '') + '?' // here
    ,checkRewrite : function () {
        var testUri = location.pathname.replace(/\/[^\/]*$/, '/rewriteTest.js').substr(1);
        function fail() {
            $('#minRewriteFailed')[0].className = 'topNote';
        };
        $.ajax({
            url : '../f=' + testUri + '&' + (new Date()).getTime()
            ,success : function (data) {
                if (data === '1') {
                    MUB._minRoot = MUB._minRoot.replace('?', ''); // here
                    $('span.minRoot').html(MUB._minRoot); // here
                } else
                    fail();                
            }
            ,error : fail
        });
    }

4 comments:

  1. Hi,
    There is a good onnline tool to minify CSS, and JS, at this link:
    refresh-sf.com/yui

    ReplyDelete
  2. These kind of post are always inspiring and I prefer to read quality content so I happy to find many good point here in the post, writing is simply great, thank you for the post.
    website design

    ReplyDelete
  3. Bluehost is ultimately the best website hosting company for any hosting services you need.

    ReplyDelete
  4. Actually i was looking for compressing java script for my blogspot website. This helps me to understand the concept easily.

    Thanks admin for this post. Keep posting quality of content.

    SEO Freelancer in Bangalore

    ReplyDelete