Sunday, June 11, 2017

Combine and minify CSS/JS files with the minify API to minimize page loading time

Page loading time is a major factor in customer satisfaction with a website as many studies have shown, e.g. read this blog post. One step towards fast loading time is to minimize the data which need to be transferred before the page can be shown to the user.

In this blog post, I will talk about my approach to combine and minimize all CSS and javascript files. Combining the files reduces the number of requests and compressing reduces the number of bytes required to send to the customer. There are build tools like  GRUNT and Gulp, which allow you to do this in a more structured way, but here I will just discuss my own quick fix.

I wrote a small python script for this task which makes use of the minify API to process the CSS and javascript files. This is how it looks:
def compressor(api_url, output_name, filenames):
    '''
    Here we use the minify API 
    js files need to be sent to api_url = https://javascript-minifier.com/raw
    css files need to be sent to api_url = https://cssminifier.com/raw
    '''
    code = []
    total_cost = 0
    for fn in filenames:
        if fn.startswith('http://') or fn.startswith('https://'):
            response = requests.get(fn)
            if response.status_code == 200:
                code.append( response.text )
            else:
                print('ERROR: "%s" is not a valid url! Exit with "
                      status code %d' % (fn, response.status_code))
                return False
        else:
            if not os.path.isfile(fn):
                print('ERROR: "%s" is not a valid file!' % fn)
                return False
            code.append( open(fn).read().decode('utf-8') )
        cost = len(code[-1]) / 1024.0
        total_cost += cost
        print("added %s with (%.2fK)" % (fn, cost))
    payload = {'input': u' '.join(code)}
    response = requests.post(api_url, payload)
    if response.status_code == 200:
        outfile = open(output_name, 'w')
        outfile.write(response.text.encode('utf-8'))
        outfile.close()

        print('-' * 50)
        print('>> output: %s (%.2fK) from (%.2fK)' %
              (output_name, len(response.text)/1024.0, total_cost))
        return 
    else:
        print('ERROR: "%s" is not a valid url! Exit with "
              status code %d' % (fn, response.status_code))
        return False
This function gets the API URL, the output file name and a list of input files. It will concatenate all input files and send the code to the minify API, where it is processed and sent back. If you provide a URL instead of a file, it will download the data for you so you can build your js file entirely with CDN links.

So what does the minify API do? Let's take an example. I have a function in my javascript which changes the style display status of an HTML element like this:
// Every question mark on the site is handled through this function
function toggle_help(target_id) {
    var target_object = document.getElementById(target_id)
    if( target_object.style.display == 'none' ){
        target_object.style.display = 'block';
    }
    else{
        target_object.style.display = 'none';
    }
} 
After feeding it into the minify API it looks like this:
function toggle_help(e){
    var t = document.getElementById(e);
    "none"==t.style.display?t.style.display="block":t.style.display="none"
}

So minify removed all comments and unnecessary whitespace. It also renames the variables to one letter names. The purpose of this is of course just to minimize the number of bytes.

It is certainly not a good idea to work with the minified code. It's an unreadable mess (in the example above I introduced some whitespaces and indentation, to make it more readable). So my approach with the function above is to write my code nicely separated in many files logically separated by functionality and only before deployment I put them all together.

To process js files you have to call the function with
api_url = https://javascript-minifier.com/raw
while for javascript files, you have to call the function with
api_url = https://cssminifier.com/raw
I put this code on GitHub. Let me know if you have any questions/comments below.
cheers
Florian

No comments:

Post a Comment