Minify CSS and JavaScript files with Visual Studio and ASP.NET Core

In the last two posts, I wrote about improving ASP.NET Core web site performance. The first one was about caching files to ensure the browser download them only once. The second is about reducing the size of the server response with gzip compression. Today, I'll stay on improving performance with bundling and minifying stylesheets and JavaScript files.

Minifying consists of reducing the size of the JavaScript and Stylesheet files by removing comments, spaces, merging CSS rules, shortening variable/function names, replacing true by !0, and lots of advanced techniques.

Bundling consists of combining multiple files together. This reduces the number of requests needed to display a web page, and so the loading time. With HTTP/2, this is less important, mainly if you can take advantages of Server push (not currently supported in .NET Core). If you are not familiar with HTTP/2, you should read this blog post from 2 members of the IIS team.

Now we understand why we should bundle and minify stylesheets and JavaScript files, let's see how we can do that with Visual Studio and ASP.NET Core.

Minifying files with Visual Studio

In the preview version of ASP.NET Core, the web site template uses Gulp to generate the min files. This tool is widely used by front-end developers to automate build tasks. However, using Node.js and gulp has also negative effects. For instance, creating a new project is longer because of installing the Node.js dependencies (about 1500 files in node_modules). It also increases the build time. Finally, every developer must install Node.js on their computer to build the project.

To remove the Node.js dependency, Mads Kristensen has written an extension for Visual Studio for minifying files: BundlerMinifier. This extension relies on NUglify, a fork of the Microsoft Ajax Minifier, to minify CSS and JS. Once you install the extension, a new menu item is available in the solution explorer:

This menu is available for CSS and JS files. After you create a bundle, a file named bundleconfig.json is added to the project. This file contains the list of bundles and some optional properties.

[
  {
    "outputFileName": "wwwroot/css/site.min.css",
    "inputFiles": [
      "wwwroot/css/site.css",
      "wwwroot/css/site2.css"
    ]
  },
  {
    "outputFileName": "wwwroot/js/site.min.js",
    "inputFiles": [
      "wwwroot/js/site.js"
    ],
    "minify": {
      "enabled": true,
      "renameLocals": true
    },
    "sourceMap": false
  }
]

The extension monitors the input files, and automatically re-generated the output file as soon as you save a file. So, when you refresh the page in your browser, the minified files are up to date.

Generating HTML

Now, the solution contains the source files and the minified files. In general, you want to use the minified files of your app only in a production environment. During development, you want to use your original files so your app is easier to debug. So, you can use the environment tag helper to use the right files depending on the environment:

<environment names="Development">
    <link rel="stylesheet" href="~/css/file1.css" />
    <link rel="stylesheet" href="~/css/file2.css" />
    <link rel="stylesheet" href="~/css/file3.css" />
</environment>
<environment names="Staging,Production">
    <link rel="stylesheet" href="~/css/bundle1.min.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/css/bundle2.min.css" asp-append-version="true" />
</environment>

Ok, this works as expected. However, I don't like this solution. First, this is very verbose (environments, append version), and we don't know which files are part of which bundle. Secondly, we have already declared the same information in the bundleconfig.json file. So why not reuse this file to generate the list of link and script tags. We can imagine something like:

<bundle name="wwwroot/css/bundle1.min.css" />
<bundle name="wwwroot/css/bundle2.min.css" />

This is much simpler, and less error prone. Great news, I've developed this tag helper and it was merged in the BundlerMinifier repository 😃. The TagHelper reads the json file, and depending on the environment, generates the script and link tags and append the file version. To use it:

  1. Install the NuGet package BundlerMinifier.TagHelpers

  2. Register the tag helpers in _ViewImports.cshtml:

    @addTagHelper *, BundlerMinifier.TagHelpers
  3. Use the tag helper. The name attribute must match the outputFileName of the bundleconfig.json file.

    <bundle name="wwwroot/js/site.min.js" />
    <bundle name="wwwroot/css/site.min.css" />
  4. Optional, you can change the configuration in the startup.cs file:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
        services.AddBundles(options =>
        {
            options.UseMinifiedFiles = false;
            options.AppendVersion = false;
        });
    }

Conclusion

The BundlerMinifier extension developed by Mads Kristensen and the bundle tag helper allow you to easily provide minified version of your CSS and JavaScript files, and so improve the loading time of your web site. This solution doesn't provide as much options as some Node.js tools, but it is easier to set up and more integrated in Visual Studio.

Leave a reply