Home > Magento > Efficient Asset Management with Multiple Merged Asset Groups

Efficient Asset Management with Multiple Merged Asset Groups

This post covers a very simple idea that I have been using internally for a while. It is a solution to the issue that nearly all Magento sites face regarding optimization of loading fewer Javascript and CSS resources (accomplished via merging) vs not loading the same resources multiple times (a side-effect of optimizing for the former). The idea is simple: instead of having only one merged JS/CSS file per page, have two or more. I’m sure the idea is not entirely new, so I’ll include some easy instructions for implementation (which is new, to my knowledge). For simplicity I will call the method “Multiple Merged Asset Groups” (MMAG).

Without going in-depth into the Magento core code mechanics, the ‘page/html_head’ block is the one that includes the logic for merging JS/CSS files. It works rather well (although it does have issues which I have thus far failed to get resolved with the core team). So for the purposes of the MMAG technique we will re-use this block with a minor change that solves two issues. One is to cause it to only render the script and style tags for the merged assets and not all of the other head content, and the other is to prevent annoying PHP warnings in the case that there are no JS or CSS files added.

<?php

/**
 * Allows for separate "groups" of merged CSS/JS files without adding to the default head block.
 */
class My_Module_Block_Assets extends Mage_Page_Block_Html_Head
{

    protected function _toHtml()
    {
        if (empty($this->_data['items'])) {
            return '';
        }
        return $this->getCssJsHtml();
    }

}

I am using here a namespace called “My_Module”, so it is left up to you, the reader, to simply copy this code into their own module and rename the namespace as appropriate. Note, this is not a “rewrite”, it is a new block that simply extends an existing block.

The next step is to include this block in the main template files wherever it is appropriate for your theme. In most cases you can just add it to the original “head” block, but if you’ve done some hefty optimization work you may want it in the body somewhere. Here is an example of adding it to the “head” block (e.g. in local.xml):

<layout>
    <default>
        <reference name="head">
            <block type="mymodule/assets" name="page_assets" />
        </reference>
    </default>
</layout>

Again, replace “mymodule” with your own module namespace as appropriate. Next, ensure that the block is actually rendered somewhere. In the base/default theme, the ‘page/html/head.phtml’ template calls “getChildHtml()” immediately after “getCssJsHtml()” so we are already good to go. Otherwise you would simply need to render the block somewhere in your template. E.g.:

<?php echo $this->getChildHtml('page_assets') ?>

All that is left now is to audit your existing layouts and look for scripts that are added in the “<default>” handle that don’t really belong there. E.g. use your IDE’s (I hope you’re using PhpStorm) Find in Files feature to search for /method=”add(Js|Css|Item)”/ in all .xml files. When you find one of these in the “<default>” handle or “head” block that doesn’t belong, change it to look like this:

<layout>
    <some_controller_action> <!-- Notice this is an action handle, *not* 'default' -->
        <reference name="page_assets">
            <action method="...">...</action>
        </reference>
    </some_controller_action>
</layout>

In some cases it may be core code or third-party code that adds the asset to the default handle OR head block when it doesn’t belong. In this case you can relocate it without modifying or overriding the module’s layout file directly by using the ‘removeItem’ method.

<layout>
    <checkout_onepage_index>
        <reference name="head">
            <action method="removeItem">
                <type>js</type><file>mage/centinel.js</file>
            </action>
        </reference>
        <reference name="page_assets">
            <action method="addJs"><file>mage/centinel.js</file></action>
        </reference>
    </checkout_onepage_index>
</layout>

The end-result should be that the first merged asset file (look at the md5 hash) should be the same no matter what page you’re on (e.g. compare a CMS page to a product page). With this technique implemented you can go back and pare down the scripts added to your default layout handle that are really only used on a few pages and add them directly to the needed action handles using the “page_assets” block.

No more cognitive dissonance!

Magento

  • Andrii Kasian

    Why do not use mod_pagespeed for apache/nginx instead?

  • http://colin.mollenhour.com Colin Mollenhour

    I have looked at mod_pagespeed and it is very cool, but opted against it a few years ago for a few reasons:

    1. mod_pagespeed does not understand the difference between Magento pages and what assets are only required on what pages, it is just a simple “combine everything” algorithm just like the core Magento method that I am unhappy with. The size limit parameter probably helps it out only by coincidence but it still is going to be sub-optimal. That is, what I want to avoid is forcing a client to download multiple copies of Prototype just because it was packaged up in more than one way with other scripts.
    2. It appears that it either doesn’t use caching or it doesn’t use real-time invalidation or depends on relative resource urls and simple server architectures.
    3. Having a slew of developers working on any given site at any given time, I don’t want to introduce more complexities into the application stack that have to be tested and accounted for. Magento JS/CSS merging works, it is efficient (with the patches I made and deployment methods) and it isn’t dependent on a web-server third-party module.
    4. I generally don’t like black-box optimizations, especially for extremely critical applications..

    I think mod_pagespeed is probably a great choice for a lot of people, especially those using applications that are poorly optimized out of the box, but it just doesn’t jive with how I roll. Same with Cloudflare (besides the fact that their uptime is sub-par).

    Thanks for the comment. Do you use mod_pagespeed with Magento sites? If so, what was your experience like?

  • Andrii Kasian

    No, I don’t use pagespeed. I prefer to use minification and compresion, so no need in external tools (and “I don’t want to introduce more complexities into the application stack” also ;) Also I use extension which moves all js scripts to page end.

  • http://www.matthias-zeis.com/archiv/magento-neuigkeiten-wochen-21-22-2014 Magento-Neuigkeiten der Wochen 21/22 2014

    […] Colin schrieb über effizientes Management von Assets. […]

  • http://colin.mollenhour.com Colin Mollenhour

    Thanks for the link. Yes, the same concept, just a different implementation. Mine doesn’t require a core rewrite or use any additional xml traversal, modify xml attributes real-time, use expensive xpath queries or depend on arbitrary node names (params). It also can be used outside of the head block. I tried to make the solution as simple as it could possibly be.