Optimizing performance of Sitecore websites with the Mavention Bundles module


Does building websites using frameworks mean poor performance?

With the advent of web frameworks, building rich websites has been easier than ever before. With only a few clicks you can extend your website with some additional functionality that someone else has already developed. Rather than reinventing the wheel, reusing existing frameworks allows you to focus on what makes your website unique. Unfortunately the consequence of using all these frameworks is an increased number of assets that need to be loaded with your website. While you could lower the impact for subsequent requests with a proper caching configuration on your server, the first, and often the most important visit, would still require a substantial number of requests to load your website.

Bundles to the rescue

ASP.NET MVC introduces the concept of bundles to merge and minify multiple Javascript or CSS files together. With ASP.NET MVC bundles you can keep your assets separate for development purposes. Once done ASP.NET MVC would merge and minify them for you automatically with little additional effort. Registering bundles in ASP.NET MVC takes place in the HttpApplication class registered in the Global.asax file. When building ASP.NET MVC applications the custom HttpApplication class is already a part of your project and it is not much of a problem to extend it with bundles definition. In Sitecore however the Global.asax file points to a Sitecore implementation of the HttpApplication class of which you obviously don’t own the code. And while you could work around that, Sitecore offers another approach for registering bundles.

Registering bundles in Sitecore

Sitecore is a highly extensible platform that uses its pipelines infrastructure to customize and control how Sitecore is working. Custom pipeline components can be used for many different purposes including registering bundles. Following is a sample code of how you could register bundles with Sitecore:

public class RegisterBundles {
    [UsedImplicitly]
    public virtual void Process(PipelineArgs args) {
        RegisterBundles(BundleTable.Bundles);
    }
 
    private void RegisterBundles(BundleCollection bundles) {
        bundles.Add(new ScriptBundle("~/bundles/js").Include(
            "~/assets/js/jquery.js",
            "~/assets/js/custom.js"));
 
        bundles.Add(new StyleBundle("~/bundles/css").Include(
            "~/assets/css/bootstrap.css",
            "~/assets/css/custom.css"));
    }
}

Registering bundles with Sitecore using a custom pipeline isn’t complex but the question is whether you want to repeat this code for every single project that you will be working on and maintaining it separately. What if you could separate the code from the bundles definitions and write the code once and merely reference it in all your projects?

Better registering bundles in Sitecore with the Mavention Bundles module

Mavention Bundles is a Sitecore module that allows you to register bundles using the Sitecore patching facilities. The module registers a custom pipeline component that during the initialization of your Sitecore website scans the configuration files for bundles definitions and registers them with the ASP.NET MVC runtime. After installing the module the only thing left for you to do, is to create an include file with the bundles definition for your website. Following is a sample include file based on the LaunchSitecore website:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <maventionBundles>
      <enableOptimizations>true</enableOptimizations>
      <bundles hint="list">
        <bundle hint="1" type="Mavention.Sitecore.Bundles.SiteBundle, Mavention.Sitecore.Bundles">
          <bundlePath>~/bundles/js</bundlePath>
          <bundleType>Script</bundleType>
          <files hint="list">
            <file hint="1">~/assets/js/bootstrap.min.js</file>
            <file hint="2">~/assets/js/custom.js</file>
          </files>
        </bundle>
        <bundle hint="2" type="Mavention.Sitecore.Bundles.SiteBundle, Mavention.Sitecore.Bundles">
          <bundlePath>~/bundles/css</bundlePath>
          <bundleType>Style</bundleType>
          <files hint="list">
            <file hint="1">~/assets/css/bootstrap.css</file>
            <file hint="2">~/assets/css/font-awesome.css</file>
            <file hint="3">~/assets/css/font-awesome-ie7.css</file>
            <file hint="4">~/assets/css/custom_base.css</file>
            <file hint="5">~/assets/css/custom_nav.css</file>
            <file hint="6">~/assets/css/custom_modules.css</file>
            <file hint="7">~/assets/css/custom_mvc.css</file>
          </files>
        </bundle>
      </bundles>
    </maventionBundles>
  </sitecore>
</configuration>

Then in your layouts you would simplify refer to these bundles:

<link href="/bundles/css" rel="stylesheet">
<script src="/bundles/js"></script>

You can define as many bundles as you need. Using the enableOptimizations element you can control whether bundles should be built and minified or not. By default all bundles will be served from the /bundles/ path which has been registered by the module during its installation, but if you need to use another path, you can register it yourself in the IgnoreUrlPrefixes Sitecore setting. By using the Mavention Bundles module you eliminate the need for writing and maintaining custom code and can focus on building your website instead. Download: Mavention Bundles module from the Sitecore Marketplace

Known issues

When requesting a bundle in the browser you get a 500 Internal Server Error error. In the Event Viewer you see the following error:

Exception message: Could not load file or assembly ‘WebGrease, Version=1.5.1.25624, Culture=neutral, PublicKeyToken=31bf3856ad364e35’ or one of its dependencies. The located assembly’s manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

This error is likely caused by the fact that your website is running on an older version of the MVC framework. You could try to solve it by adding the following assembly binding redirect to your web.config:

<dependentAssembly>
    <assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" culture="neutral" />
    <bindingRedirect oldVersion="0.0.0.0-1.6.5135.21930" newVersion="1.6.5135.21930" />
</dependentAssembly>
Others found also helpful: