I recently did some work on a large web application that had several .js and .css files. Per industry best practice, I wanted to use something like YUI Compressor or Google Closure Compiler to merge and minify all the JavaScript and CSS into a single .js file and a single .css file. Also, I wanted an easy way to switch from these optimized files–which are hard to read–to the original files during development (or to debug an issue in production).
In this case, the JS/CSS needed to be minified at build-time. The project was built via Maven, so it made sense to do this with a Maven project. After a lot of research, I chose a plugin called wro4j because it allows you to use an external XML file to configure exactly which files should be minified/optimized. And by making this XML file part of the website itself, you can use the information it contains (i.e., the list of individuals .js and .css files) to dynamically switch from the compressed files to the “raw” ones. The main benefit? The list of .js and .css files exists in only one place–the XML config file. Here’s what it looks like:
/css/reset.css /css/master.css /css/typography.css /js/lib/swfaddress/swfaddress.js /js/constants.js /js/util.js ...
As I said, this configuration is used by the wro4j plugin when it minifies/compresses/optimizes the code at build-time. I won’t go into the details of how to configure wro4j here; see the wro4j website for info on that.
So assuming you have wro4j configured to generate myapp-all-compressed.js
and myapp-all-compressed.css
, the next thing you’ll do is modify your HTML doc to use those files:
...
So how can we easily switch from these hard-to-read files to the “original” files? We’ll use JavaScript to download the aforementioned XML config file (resources.xml), parse it, and dynamically add
...
As you can see, navigating to http://yoursite.com/
results in the “optimized for production” files being used, but something like http://yoursite.com/?debug
causes the browser to instead download loadIndividualJsCssFiles.js
and call myapp.util.loadIndividualJsCssFiles()
with the path to resources.xml.
Finally, let’s take a look inside loadIndividualJsCssFiles.js. At a high level, it extracts the list of .js/.css files from resources.xml and dynamically adds
if( myapp === undefined ) { myapp = {}; } if( myapp.util === undefined) { myapp.util = {}; } /** * Use this function to dynamically load a single CSS file. * * @param {String} path - URL path to the .css file that should be loaded */ myapp.util.loadStylesheetFile = function(path) { var linkElement = document.createElement('link'); linkElement.setAttribute("type", "text/css"); linkElement.setAttribute("rel", "stylesheet"); linkElement.setAttribute("href", path); // Append the new element to the end of . The browser should // start downloading it immediately. myapp.console.info('Loading '+ path); document.getElementsByTagName("head")[0].appendChild(linkElement); }; /** * Use this function to dynamically load a single JavaScript file. * * @param {String} path - URL path to the .js file that should be loaded * @param {Object} callbackFcn - (optional) A function that will be called if/when the script finishes loading */ myapp.util.loadJavaScriptFile = function(path, callbackFcn) { var elementOnLoadFcn, scriptElement = document.createElement('script'); scriptElement.setAttribute("type", "text/javascript"); if( callbackFcn != undefined && callbackFcn != null ) { // Create a function that will be used as the value for the
The end result is that you keep the list of .js/.css files in one place (resources.xml)–you don’t have to keep updating your .jsp or pom.xml file each time you need to add or remove .js/.css files. The loadJavaScriptFiles()
function will ensure those files get picked up the next time you refresh the browser with the ‘debug’ URL param, and wro4j will similarly include those files in myapp-all-compressed.js/myapp-all-compressed.css the next time a build is done.