46 Comments

Optimizing for website performance includes setting long expiration dates on our static resources, such s images, stylesheets and JavaScript files. Doing that tells the browser to cache our files so it doesn’t have to request them every time the user loads a page. This is one of the most important things to do when optimizing websites.

In ASP.NET on IIS7+ it’s really easy. Just add this chunk of XML to the web.config’s <system.webServer> element:

<staticcontent>
  <clientcache cachecontrolmode="UseMaxAge" cachecontrolmaxage="365.00:00:00" />
</staticcontent>

The above code tells the browsers to automatically cache all static resources for 365 days. That’s good and you should do this right now.

The issue becomes clear the first time you make a change to any static file. How is the browser going to know that you made a change, so it can download the latest version of the file? The answer is that it can’t. It will keep serving the same cached version of the file for the next 365 days regardless of any changes you are making to the files.

Fingerprinting

The good news is that it is fairly trivial to make a change to our code, that changes the URL pointing to the static files and thereby tricking the browser into believing it’s a brand new resource that needs to be downloaded.

Here’s a little class that I use on several websites, that adds a fingerprint, or timestamp, to the URL of the static file.

using System; 
using System.IO; 
using System.Web; 
using System.Web.Caching; 
using System.Web.Hosting;

public class Fingerprint 
{ 
  public static string Tag(string rootRelativePath) 
  { 
    if (HttpRuntime.Cache[rootRelativePath] == null) 
    { 
      string absolute = HostingEnvironment.MapPath("~" + rootRelativePath);

      DateTime date = File.GetLastWriteTime(absolute); 
      int index = rootRelativePath.LastIndexOf('/');

      string result = rootRelativePath.Insert(index, "/v-" + date.Ticks); 
      HttpRuntime.Cache.Insert(rootRelativePath, result, new CacheDependency(absolute)); 
    }

      return HttpRuntime.Cache[rootRelativePath] as string; 
  } 
}

All you need to change in order to use this class, is to modify the references to the static files.

Modify references

Here’s what it looks like in Razor for the stylesheet reference:

<link rel="stylesheet" href="@Fingerprint.Tag("/content/site.css")" />

…and in WebForms:

<link rel="stylesheet" href="<%=Fingerprint.Tag(" />content/site.css") %>" />

The result of using the FingerPrint.Tag method will in this case be:

<link rel="stylesheet" href="/content/v-634933238684083941/site.css" />

Since the URL now has a reference to a non-existing folder (v-634933238684083941), we need to make the web server pretend it exist. We do that with URL rewriting.

URL rewrite

By adding this snippet of XML to the web.config’s <system.webServer> section, we instruct IIS 7+ to intercept all URLs with a folder name containing “v=[numbers]” and rewrite the URL to the original file path.

<rewrite>
  <rules>
    <rule name="fingerprint">
      <match url="([\S]+)(/v-[0-9]+/)([\S]+)" />
      <action type="Rewrite" url="{R:1}/{R:3}" />
    </rule>
  </rules>
</rewrite>

You can use this technique for all your JavaScript and image files as well.

The beauty is, that every time you change one of the referenced static files, the fingerprint will change as well. This creates a brand new URL every time so the browsers will download the updated files.

FYI, you need to run the AppPool in Integrated Pipeline mode for the <system.webServer> section to have any effect.

6 Comments

Update Jan 1st 2014. TypeScript support is back in Web Essentials!

When Web Essentials was ported to Visual Studio 2013, a lot of features were removed. This was because many of those features were built directly in to Visual Studio 2013 and therefore no longer needed support by Web Essentials. That’s not exactly the reason why the support for TypeScript was removed.

TypeScript has been evolving at a steady pace since it was released in the fall of 2012. It’s now up to version 0.9 and that means that the final 1.0 release is getting closer. The Compile on save feature introduced by Web Essentials 2012 has now been rolled into TypeScript and the feature will be broadened in the months to come. That means Compile on save will be natively available for more project types, including ASP.NET projects, in the foreseeable future. If both Web Essentials and the TypeScript tooling provide the exact same functionality, there is bound to be conflicts.

You might remember what happened when LESS and CoffeeScript tooling was moved from Web Essentials into Visual Studio 2012 Update 2. Unless you updated both at the same time, Visual Studio would crash. The Visual Studio Web Team had to write a blog post about it and even ScottGu mentioned it. It was bad. Really bad.

In order not to repeat the same mistake twice, we’ve opted for not having TypeScript tooling in Web Essentials. That’s not to say we’ll never add extra TypeScript features in Web Essentials, but it means that we are doing what we can to avoid any collisions between the two toolsets.

As a TypeScript user you should take this as a positive thing, because it means that TypeScript is getting closer to its final 1.0 release and it will have some of the features you used to rely on Web Essentials to provide.

3 Comments

imageIn Web Essentials 1.7 for Visual Studio 2013 it is now easier than ever to work with images. In this video I'm demoing a lot of new features including:

  • Paste image from clipboard onto VS editor
  • Lossless image optimization
  • Create image sprites
  • Sprites are better with LESS/SCSS
  • Base64 encode images
  • Optimize base64 encoded images

Image workflow in Web Essentials and Visual Studio 2013

    More features were added to Web Essentials than what I'm showing in the demo video. These features include:

    • Paste image from clipboard onto CSS, LESS, SCSS, JavaScript, TypeScript, Markdown
    • Save base64 encoded image back to disk
    • Re-embed existing image as base64 encoded dataURI
    • Adding more images to a sprite
    • Changing an existing sprite without breaking your site
    • Optimize image from the HTML editor (Smart Tag on <img> element)
    • Base64 encode images from a Smart Tag in the HTML editor
    • Save base64 encoded image to disk from HTML editor
    • Drag 'n drop existing image onto Markdown editor

    Please let me know what you think about these new image workflow features.

    10 Comments

    What's the address of your website? www.domain.com or domain.com?

    There are two camps on the subject of the www subdomain. One believe it should be enforced (www.yes-www.org) and the other (no-www.org) that it should be removed. They are both right.

    What's important is that there is only a single canonical address to your website – with or without www.

    The web.config makes it easy for us to either enforce or remove the www subdomain using URL rewrites. There are many examples online on how to do this, but they all share 2 fundamental flaws. The rules have a direct dependency to the domain name and they don't work with both HTTP and HTTPS.

    So let's see if we can create generic URL rewrite rules that can be used on any website without modifications.

    Your server needs to have the URL Rewrite module installed. Chances are that it does already. Azure Websites does and so does all of my other hosting providers.

    Rewrite rules need to be placed inside the <rewrite> element in web.config:

    <system.webServer>
      <rewrite>
        <rules>
          <!-- My rules -->
        </rules>
      </rewrite>
    </system.webServer>
    

    So here are 2 rules that works on all domains and on both HTTP and HTTPS.

    Remove WWW

    This rule redirects any incoming request to www.domain.com to domain.com while preserving the HTTP(S) protocol:

    <rule name="Remove WWW" patternSyntax="Wildcard" stopProcessing="true">
      <match url="*" />
      <conditions>
        <add input="{CACHE_URL}" pattern="*://www.*" />
      </conditions>
      <action type="Redirect" url="{C:1}://{C:2}" redirectType="Permanent" />
    </rule>
    

    Enforce WWW

    This rule redirects any incoming request to domain.com to www.domain.com while preserving the HTTP(S) protocol:

    <rule name="Enforce WWW" stopProcessing="true">
      <match url=".*" />
      <conditions>
        <add input="{CACHE_URL}" pattern="^(.+)://(?!www)(.*)" />
      </conditions>
      <action type="Redirect" url="{C:1}://www.{C:2}" redirectType="Permanent" />
    </rule>
    

    So there you have it. It's easy once you now how.

    For more info on the URL Rewrite Module, see the Configuration Reference.

    29 Comments

    imageThe _references.js file controls a key part of the JavaScript Intellisense in Visual Studio for web projects and can mean the difference between awesome Intellisense and none at all.

    This is the story of how it works and why it even exist.

    A new JavaScript editor

    The story begins shortly after the release of Visual Studio 2010. The ASP.NET & Web Tooling Team (the team I'm part of) was handing over the ownership of the JavaScript editor to a newly formed Client Platform team.

    This team was going to create a completely new and modern JavaScript editor for Visual Studio 2012 – one that was aligned with the efforts around delivering the tooling experience for the upcoming HTML/JavaScript based Windows 8 Store apps.

    The now old JavaScript editor was retired from Visual Studio, but found new life in WebMatrix, where it received further development and updates.

    As the work progressed with the new editor, it was time to redesign some of the many features we've all gotten accustomed to. One of them was Intellisense.

    Three types of Intellisense

    There are several ways of implementing Intellisense for JavaScript. The three I remember being discussed were:

    1. All .js files in the project are automatically included in Intellisense
    2. Only .js files included on the same HTML pages are included
    3. The user can manually reference other .js files

    By including all .js files in Intellisense means that there is potential for a lot of false positives. By that I mean that you'll get Intellisense coming from .js files that are not loaded on the same page as the .js file you're currently editing. It's sort of the same as getting C# Intellisense for types that hasn't been imported with a using statement – it would break at runtime.

    Loading all .js files automatically in Intellisense also came with additional issues. One was performance. The more files to parse, the heavier Intellisense became and it could hurt the user experience. Another was ordering of the .js files. The new editor runs all the code, so correct ordering of when to run each .js file was an important factor for accuracy.

    However, bundling, minification and dynamically loaded JavaScript would make it impossible for any IDE to statically figure out what .js files to provide Intellisense for. The result could potentially be that no Intellisense from other .js files got loaded at all, which was arguably worse than loading too much Intellisense.

    Finally, letting the user manually add references to other .js files eliminates performance issues and the parsing of HTML files to follow <script> references. For ASPX files that also meant following references to master pages to find <script> references. The problem with relying on the user to add references is that it puts the burden on the user.

    So there were pros and cons to each approach.

    Explicit and implicit references

    It was decided that the new editor should use a combination of option 2 and 3. It both follows <script> references and allows the user to manually add an explicit triple-slash references. Triple-slash references look like this and can be added to the top of any .js file:

    /// <reference path="../app/respond.js" />

    Dragging a .js file from Solution Explorer onto an open .js document will automatically insert a triple-slash reference at the top of the document. By adding this reference, Visual Studio will now always include Intellisense coming from the referenced file, regardless of any other conditions.

    _references.js is global

    The issue with explicitly referencing other .js files is that you'll end up adding reference to jQuery more or less on all your .js files. That's not cool. It was important to keep the burden on the user at a minimum, so a global reference cache was needed.

    Enter _references.js.

    This file must (by default) be located in a folder at the root called /scripts/. That's the naming convention. Any file located at /scripts/_references.js is automatically added to global Intellisense. This is now the only file we need for triple-slash references. Here's what the contents of this file may look like:

    /// <reference path="modernizr-2.6.2.js" />
    /// <reference path="jquery-1.10.2.js" />
    /// <reference path="bootstrap.js" />
    /// <reference path="respond.js" />

    Just a bunch of references. This is also the only file that is included in Intellisense by default at all times.

    The cool thing is that the user now only have to control Intellisense in one central location. Burden minimized.

    Change naming convention

    Some people don't like to use a folder named /scripts/ or the name _references.js. That's perfectly fine and Visual Studio offers a way out.

    Let's imaging that someone prefers the global reference file to be located at /js/globals.js – not just for one project, but in general.

    Get to the Quick Launch box by hitting CTRL+Q. Then type javascript refand select this option in the dropdown:

    image

    In the settings window that pops up, select Implicit (Web) in the dropdown:

    image

    Implicit (Windows) is for Windows Store apps and has no effect on web projects. The same is true vice versa.

    At the bottom of the list, you can see that there is a reference to /Scripts/references.js:

    image

    So now you can see what the path syntax looks like and you can therefore easily add a new path ~/js/globals.js. You can add as many as you'd like. If the file doesn't exist, Visual Studio will just ignore it.

    A tilde followed by a forward slash (~/) is referred to as tilde-slash and marks the root of a website.

    Automating it all

    All this shipped with Visual Studio 2012, but when it was time to focus on Visual Studio 2013, we decided that this workflow should be improved.

    It wasn't ok that developers should maintain this file manually. JavaScript files are added, deleted and renamed all the time, so it's very easy to forget to update the _references.js file accordingly.

    A better approach was to add a feature that would do that automatically and thereby give Intellisense for all .js files globally all the time. However, we knew that both performance and file ordering could be an issue, so it was important that this feature could be disabled in case of trouble.

    We didn't want a setting, since Visual Studio doesn't have per-project settings. And this was indeed a per-project challenge. So we introduced a new triple-slash concept – auto-sync.

    It looks like this and is located at the very top of _references.js:

    /// <autosync enabled="true" />

    This new auto-sync comment is all it takes to enable automatic synchronization of triple-slash references. Whenever a .js file is added, renamed, moved or deleted, the _references.js file is automatically updated to reflect the changes.

    Right-clicking on the _references.js file in Solution Explorer now gives you two new options:

    image

    The first is the Auto-sync JavaScript References. Clicking this menu item adds the auto-sync comment so you don't have to type it manually. It's convenient, discoverable and you only have to set it once per project.

    The second menu item is Update JavaScript References. This will add all .js files in the project as triple-slash references in the _references.js file. It updates only once and it doesn't enable auto-sync.

    With these two new commands, you can now decide if you want auto-sync or if you prefer to just update the references whenever you are in the mood.

    One caveat to enabling auto-sync of _references.js is that the references are potentially ordered in the wrong way. It could cause issues, but it probably won't. Personally, I haven't had any issues and I've used this in many projects now. But just in case it does cause trouble, now you know how to turn it off.

    So with Visual Studio 2013 we end up having all three approaches to JavaScript Intellisense.

    Generate a _references.js file

    If you don't have a _reference.js file in your project, but you have Web Essentials 2013 installed, then it's really easy to add one. Simply right-click the /Scripts/ folder and go to the Add submenu to find an easy way to add references.js.

    _references

    An added benefit with this approach is that the new _reference.js file has auto-sync enabled already.

    So, that was the story of _references.js and how it came to be. There are always room for improvements, so our job is never done. The work continues…