CSS/Javascript Aggregation/Compression
#1
Posted 17 July 2012 - 09:58 AM
Drupal does this wonderfully via a drupal_add_head() function that is used by themes and modules to add css and javascript to the head of the site. The theme then prints out a $head variable, instead of the hard-coded css and javascript includes.
This allows you to enable a perfomance option of aggregation and compression. This then combines the files together, saves them on the server and links to the aggregated files instead of the individual files.
This results in huge performance gains, especially if your site is using a css grid and any jquery plugins. Since each one comes with its own file and sometimes css styles.
I will do some research into how Drupal processes the css and js files. But in the meantime, any feedback on where this fits into processwire would be appreciated.
#2
Posted 17 July 2012 - 10:17 AM
#3
Posted 17 July 2012 - 11:21 AM
It used to be a bit of a hassle to use in the early days, but it's really simple now and shrunk my JS files to less than half their original size, even squeezing already-minimised versions of the jQuery core down further (presumably these are compresse some other way).
#4
Posted 17 July 2012 - 11:41 AM
$config->scripts->add(pathto.js) $config->styles->append(pathto.css);
Then the output
<?php foreach($config->styles->unique() as $file) echo "\n <link type='text/css' href='$file' rel='stylesheet' />"; ?> <?php foreach($config->scripts->unique() as $file) echo "\n <script type='text/javascript' src='$file'></script>"; ?>
This way a module could also add script using this. But there's no front-end module that does use it. It's up to the site builder.
@somartist | modules created | support me, flattr my work flattr.com
#5
Posted 17 July 2012 - 12:15 PM
#6
Posted 17 July 2012 - 12:20 PM
Edit: Not excluding that there could be a simple module helping setting it up. Not sure if that would make sense though.
@somartist | modules created | support me, flattr my work flattr.com
#8
Posted 17 July 2012 - 03:14 PM
<link rel="stylesheet" href="<?php echo $config->urls->templates?>css/style.css">
is accepted by H5BP's build script (ant version) without any modification. The only thing I need to do is add all .inc files containing CSS/JS references (I like to put JS references at the bottom of the page) to the build script's project.properties files.
That being said … well, with PW's user base (hopefully) growing, it will attract more "non-expert users", which is by no means a bad thing. But to these users, a module is usually much easier to use than an external PHP script or a build script which requires additional software to be installed on their system. So in the long run, PW might actually benefit from having that kind of module, if only because it makes it even easier to create well-performing sites.
#9
Posted 20 July 2012 - 11:29 AM
/**
* Helper function to minify and gzip files.
*/
function _javascript_aggregator_minify($scripts) {
// Only process it is JavaScript Optimization is enabled.
if (variable_get('preprocess_js', 0)) {
// Strip out the aggregated JavaScript file.
$path_to_files_directory = base_path() . file_directory_path();
$pattern = "!(<script type=\"text\/javascript\" src=\"(.*?)$path_to_files_directory)(.*?)(\"(.*?)><\/script>)!";
if (preg_match_all($pattern, $scripts, $matches) > 0) {
$aggregated_file_name = $matches[3][0];
$jsmin_file_name = $aggregated_file_name .'min.js';
// Construct the final JSMin file path.
$jsmin_file_path = file_directory_path() . $jsmin_file_name;
// Create the JSMinified file if it doesn't exist yet.
if (!file_exists($jsmin_file_path)) {
if (variable_get('javascript_aggregator_jsminplus', FALSE)) {
// JSMin+ the contents of the aggregated file.
require_once(drupal_get_path('module', 'javascript_aggregator') .'/jsminplus.php');
// Strip Byte Order Marks (BOM's) from the file, JSMin+ cannot parse these.
$file = str_replace(pack("CCC", 0xef, 0xbb, 0xbf), "", file_get_contents(file_directory_path() . $aggregated_file_name));
$contents = JSMinPlus::minify($file);
}
else {
// JSMin the contents of the aggregated file.
require_once(drupal_get_path('module', 'javascript_aggregator') .'/jsmin.php');
$contents = JSMin::minify(file_get_contents(file_directory_path() . $aggregated_file_name));
}
// Code comments containing copyright notices and licensing information
// are stripped when the file is minified. GPL and most other open
// source licenses require the license text to be included whenever the
// file is distributed, so include a reference to the un-minified file.
$contents = '// Minified using Javascript Aggregator - see '. $path_to_files_directory . $aggregated_file_name ." for original source including licensing information.\n". $contents;
// GZip the JavaScript if required.
$htaccess = file_directory_path() . '/js/.htaccess';
if (variable_get('javascript_aggregator_gzip', FALSE)) {
// Create the GZip file if it doesn't already exist.
if (!file_exists($jsmin_file_path .'.gz')) {
file_save_data(gzencode($contents, 9), $jsmin_file_path .'.gz', FILE_EXISTS_REPLACE);
}
// Make sure the .htaccess file is active to handle GZipped JavaScript files.
if (!variable_get('javascript_aggregator_no_htaccess', FALSE) && !file_exists($htaccess)) {
$rewrite_base = base_path() . file_directory_path() .'/js/';
$htaccess_contents = <<<EOT
<Files *.js.gz>
AddEncoding x-gzip .gz
ForceType text/javascript
</Files>
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteBase $rewrite_base
RewriteCond %{HTTP_USER_AGENT} !".*Safari.*"
RewriteCond %{HTTP:Accept-encoding} gzip
RewriteCond %{REQUEST_FILENAME}.gz -f
RewriteRule ^(.*)\.js $1.js.gz [L,QSA]
</IfModule>
EOT;
file_save_data($htaccess_contents, $htaccess, FILE_EXISTS_REPLACE);
}
}
else {
// Delete .htaccess file so *.gz files do not get served.
if (file_exists($htaccess)) {
file_delete($htaccess);
}
}
// Save the contents to the JavaScript file.
file_save_data($contents, $jsmin_file_path, FILE_EXISTS_REPLACE);
}
// Replace the aggregated file with the minified JavaScript file.
$scripts = str_replace($aggregated_file_name, $jsmin_file_name, $scripts);
}
}
return $scripts;
}
I am not a very experienced coder, so I want to know what your take is on this approach. The drupal module can be downloaded here: http://ftp.drupal.or...tor-6.x-1.6.zipMaybe we could adapt this to processwire and expand its functionality to css files as well.
#10
Posted 20 July 2012 - 12:11 PM
I've got it working on one site already, but I don't know that it's really worth a module to be honest since its already easy to set up (there's a helper script included that pretty much builds the URL for you).
I just put mine in somewhere like /site/lib/min/ and put the relevant URL in head.inc for the JS and CSS.
A module could save a little time, but in the case of Minify it would be about ten seconds
EDIT: I did just think of a way a module could save a lot more time actually so I might build this module soon.
EDIT 2: my first thought was to make it inject the relevant code before the head tag but that's nt always ideal plus you don't always want all scripts/stylesheets in every template. I think I might just make it give the relevant code to copy and paste into the template instead.
#11
Posted 20 July 2012 - 02:20 PM
Looking at minify further, perhaps we can use soma's concept and feed the files to minify.
ProcessWire has the functionality to add scripts and css using $config var.
$config->scripts->add(pathto.js) $config->styles->append(pathto.css);
Then the output<?php foreach($config->styles->unique() as $file) echo "\n <link type='text/css' href='$file' rel='stylesheet' />"; ?> <?php foreach($config->scripts->unique() as $file) echo "\n <script type='text/javascript' src='$file'></script>"; ?>
This way a module could also add script using this. But there's no front-end module that does use it. It's up to the site builder.
But since this approach will require a site builder to adapt their code to use config instead of plain old link and script tags, it is probably not favourable. I think most logical approach would be for the module to preg_match_all linked css and javascript files, serve the urls to minify and then replace the individual linked references with the minify'd references. Is there a hook that would let a module modify the page markup before rendering the page? Or would the approach I mentioned need to involve code in the theme.
#12
Posted 20 July 2012 - 02:31 PM
The whole ethos of ProcessWire is not making assumptions about what people are going to do with it or how they're going to work, so reducing the Minify process to a simple copy and paste is about as far as I feel comfortable taking it. There are occasions where you won't want every JS or CSS file being called for every page so I don't want it to make assumptions or rewrite anyone's HTML markup if I can help it.
It'll be a simple solution though - I promise
You will always have code in your templates by the way so that's not a problem. Take a look through the default template files in the site/templates directory to see what I mean.
#15
Posted 20 July 2012 - 03:30 PM
Module should be ready tomorrow or Sunday at the latest.
#16
Posted 21 July 2012 - 09:18 AM
#17
Posted 21 July 2012 - 09:11 PM
What I meant was different files for different templates, so on one site I load extra JS and CSS on a page containing a Google Map and in that case I simply set that page to use a different template and in the head.inc file check the current page's template and - if it matches 'map' for example - output the relevant extra JS and CSS.
Module should be ready tomorrow or Sunday at the latest.
Thanks, I was wondering the best way to do that. Switching on the template in header.inc is a great solution. Taking a look at the module right now.
0 user(s) are reading this topic
0 members, 0 guests, 0 anonymous users













