ASP.NET 3.5 AJAX Script Combining

Excerpt from Professional ASP.NET 3.5 AJAX 

Script compression allows you to reduce the size of scripts sent to the client, but it doesn’t reduce the number of separate scripts an ASP.NET AJAX page may be using. Besides the framework scripts MicrosoftAjax.js (the core framework) and MicrosoftAjaxWebForms.js (for partial rendering), there may be additional script references specified in the ScriptManager. ASP.NET AJAX compatible controls and components on the page may each require additional scripts that are embedded in their respective assemblies, adding even further to the number of scripts the page requires. A page can easily require a dozen or more scripts, even if there are no script references declared.
 

Script references are rendered as <script> elements at the top of the form. Each reference is processed by the browser, which issues a request to the server. Requests involve overhead to process, and involve network latency. If there are a dozen script references, there will be a dozen requests to the server, each with its own overhead and network latency. Script references are typically cached on the client thereafter, but if many of your users are first-time visitors that do not have a saturated cache, that may not be of much help.
It may not seem so bad. If the browser requested all 12 scripts simultaneously, they could load in parallel. However, that is not how the browser does it. Since one script may depend on another, the browser must load them in sequential order. Also, since scripts may contain calls to document.write, browsers load them each synchronously. Even if the browser could load them in parallel, it is limited to just two active connections to a single domain at a time.
All of this means that the user must wait for scripts to download and execute before the page even becomes visible, which can cause the page to feel slow to load. ScriptManager allows you to reduce the number of scripts your page requires by giving you the ability to combine scripts from different sources into one, automatically. To do so, simply declare the script references inside the CompositeScript element:
 
asp Code:
<asp:ScriptManager runat=”server” ID=”ScriptManager1”>
    <CompositeScript>
        <Scripts>
            <asp:ScriptReference Name=”MicrosoftAjax.
js” />
            <asp:ScriptReference Name=”MicrosoftAjaxWebForms.
js” />
        </Scripts>
    </CompositeScript>
</asp:ScriptManager>
 
If your page uses an UpdatePanel, it requires at least these two scripts. This combines them into one. In this case, the scripts are resources built into the framework, but you can also specify your own embedded scripts and path-based scripts, or any combination of these.
 
asp Code:
<asp:ScriptManager runat=”server” ID=”ScriptManager1”>
    <CompositeScript>
        <Scripts>
            <asp:ScriptReference Name=”MicrosoftAjax.
js” />
            <asp:ScriptReference Name=”MicrosoftAjaxWebForms.
js” />
            <asp:ScriptReference Name=”MyEmbeddedScript.
js” Assembly=”MyAssembly” />
            <asp:ScriptReference Path=”~/PathBasedScript.
js”  />
            <asp:ScriptReference Name=”ComponentEmbeddedScript.
js
                Assembly=”ComponentAssembly” Path=”ExtractedComponentScript.
js” />
        </Scripts>
    </CompositeScript>
</asp:ScriptManager>
 
MyEmbeddedScript.js is an embedded resource in the MyAssembly, PathBasedScript.js  is a static script file in the root of the Web application, and ComponentEmbeddedScript.js is an embedded resource in a referenced assembly from a third-party component that has been extracted and put on disk for customization. All of these script references work as they normally do, but they are all combined into a single script on the server, and rendered as a single reference to the ScriptResource.axd handler.

 Determining Which Scripts to Combine

You might wonder why all scripts for a given page aren’t just automatically combined by the framework, all the time. During development, the ASP.NET AJAX team considered an automatic combining option. At first, that seems like a good idea. With very little effort, you could quickly and easily reduce your page’s script count. However, the team quickly realized that it’s actually possible for combining scripts to hurt the overall performance of your site. Each combination of scripts forms a unique script, separately cached by the browser. It requires careful consideration of the scripts you are using and how users typically navigate through your site to get the maximum benefit out of script combining.

Consider the following example. Your site consists of two pages; Page 1 and Page 2. Page 1 uses scripts A, B, and C. Page 2 uses scripts C and D. If on Page 1 you combined scripts A, B, and C, and on Page 2 you combined C and D, then upon navigating to Page 2, the user will actually be downloading script C for the second time! The individual performance of each page is better on its own than it is with no combining. However, without combining, the user would only have had to download script D when going to Page 2, so the actual performance of Page 2 is slower with full script combining. More bandwidth is used, as well.

What can you do to improve performance of each page and of the site in general? As you can see, it depends largely on the usage pattern of your site. You could combine only A and B on Page 1. Then, users would get two scripts on Page 1—the combined A and B, and separately, C. When navigating to Page 2, they’d only need to download script D. This reduces redundant bandwidth downloading script C, but Page 1 still has two scripts, and if someone navigates directly to Page 2, there are also two scripts. So things are better than without any script combining, but perhaps there is room for improvement.

There is no golden rule with script combining, just some general guidelines you can follow:
 

  • Think of your pages as groups of functionality. If there’s a certain set of pages that users are likely to visit together in one session (such as a search results page and a detail page), and they all require about the same scripts, it may be best to use a combination of scripts common to all pages in that set. For example, combine scripts A, B, C and D on both Page 1 and 2. Whatever page the user hits first, they “front load” all the scripts they are likely to use in this session. Upon navigating to another page in the group, there are zero scripts to download.
     
  • Avoid combinations that are different but contain a common script, as it will cause the common script to be downloaded separately with each combination.
     
  • Keep script combinations in a particular order. If there are two combinations that contain the same scripts but in a different order, they each have different URLs, and so are cached separately.
     
Combining Scripts Manually

Just like normal ScriptReferences, the CompositeScript element of the ScriptManager supports the Path attribute. By specifying scripts within a CompositeScript element and specifying the path to a static script file, you are substituting all scripts in the combination for the static script.
 
asp Code:
<asp:ScriptManager runat=”server” ID=”ScriptManager1”>
    <CompositeScript Path=”~/Scripts/CombinedScript.
js”>
        <Scripts>
            <asp:ScriptReference Name=”MicrosoftAjax.
js” />
            <asp:ScriptReference Name=”MicrosoftAjaxWebForms.
js” />
            <asp:ScriptReference Name=”MyEmbeddedScript.
js” Assembly=”MyAssembly” />
            <asp:ScriptReference Path=”~/PathBasedScript.
js”  />
            <asp:ScriptReference Name=”ComponentEmbeddedScript.
js
                Assembly=”ComponentAssembly” Path=”ExtractedComponentScript.
js” />
        </Scripts>
    </CompositeScript>
</asp:ScriptManager>
 
In this example, all the script references are combined into the CombinedScript.js in the Scripts directory, a task that you must do yourself rather than allowing ScriptManager to do automatically. This really maximizes the performance of your page! Not only are all the scripts combined into one, they are loaded statically from disk, which involves less overhead than retrieving it from the ScriptResource.axd handler. It also reduces the size of the rendered HTML, since multiple script elements are now one, and its URL is just a short path. Automatically combined scripts result in a long URL, because information about each of the scripts is encoded within it. Of course, this feature requires that you combine the scripts yourself. The simplest way to do that is—you guessed it—use script combining! Simply specify your script references, view the page in the browser, look at the HTML source, paste the ScriptResource.axd URL into the browser’s address bar, and you have your combined script.

The Script Reference Profiler

It’s not always obvious what scripts your page requires because you may be using a number of third-party components that register scripts automatically with the ScriptManager. Viewing the HTML source of your page doesn’t help much, since those scripts are likely embedded resources, and so are served through a ScriptResource.axd  URL, which doesn’t give any clues as to which script it is for. Conceivably, you could use the ResolveScriptReference event of the ScriptManager to determine all the scripts required by your page. But thankfully, a tool already exists: the Script Reference Profiler. Just put the Script Reference Profiler control on your page, and it lists exactly the scripts that have been registered. Then all you need to do is copy and paste the list into the CompositeScript reference. You can download the tool from codeplex.com/aspnet/Release/ProjectReleases.aspx?ReleaseId=13356.

Compatibility

Those who have written custom controls for ASP.NET 2.0 may look at the ScriptManager in ASP.NET 3.5 and wonder why many of the APIs are the same as what already exists on the ClientScriptManager object. The ASP.NET 2.0 ClientScriptManager was written to support postbacks and callbacks where the burden of partial page updates was left to the control developer. The ScriptManager focuses on enabling partial page rendering for controls natively. To allow the ScriptManager to effectively update parts of the page, custom controls need to use its methods for registering script so that partial page updates will correctly manage the script with the browser.

Controls can typically be updated to work with partial rendering by simply exchanging the calls to the ClientScriptManager for calls to the new ScriptManager control. For example, the ASP.NET validator controls registered JavaScript with the ClientScriptManager and would fail to execute correctly during partial page updates but work correctly after being updated to call the equivalent ScriptManager controls. The ScriptManager methods for script registration are static and have the same signature as those on the ClientScriptManager, except for their first parameter. The first parameter is a control, and is meant to be set to the control that is registering the resource or the page if it is page code doing the registration. This allows ScriptManager to determine which controls on a page require which resources.

 

 

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *