Tuesday 29 December 2015

Minimal Download Strategy in SharePoint 2013

Over the past decade, web-based applications have developed to the point that each user interaction requires very little data to be downloaded as pages change and refresh. A prime example is Facebook, where each user interaction (such as the addition of a comment) results in an update of only that portion of the page, improving performance and response time considerably.
SharePoint is based on a very rich web interface containing lots of moving parts and lots of customizable areas. As you know the Interweb is based on a request-response approach where you request a page and get a response back. The approach is the same whether you update the whole page or portions of it. The last decade we've seen smart approaches working around this "problem" using DHTML, JavaScript, AJAX, Update Panels, you name it. Facebook, which also contains a very rich and dynamic interface, has been excellent in making a dynamic web interface minimizing the amount of data downloaded for each user interaction and in this way also increasing the perceived performance.
Up until now SharePoint (out-of-the-box) has not been that efficient on this, even though SharePoint 2010 did some improvements. Most often the whole page has been reloaded for each and every action; this of course affects all end-users and the perceived performance of SharePoint.
But now, with SharePoint 2013, we have a whole new paradigm thanks to the Minimal Download Strategy - MDS. In this post I will walk you through some details of MDS, how to leverage it in your own customizations and how to successfully adapt your current and future customizations to MDS.

The Minimal Download Strategy (MDS) - an overview

The Minimal Download Strategy is by default enabled on Team sites, Community sites and a few others in SharePoint 2013.All pages for that site is rendered "through" the /_layouts/15/start.aspx page. This page is responsible for loading pages from the site using the MDS. The start.aspx page has a specific JavaScript object asyncDeltaManager (defined in start.js). Basically it parses the URL, looks for the # sign and takes the path following that and dynamically loads that page.
http://polasoft/_layouts/15/Start.aspx#/SitePages/Home.aspx

Subsequent requests to pages are dynamically loaded through the asyncDeltaManager object. Once a link is clicked it will invoke a method in the JavaScript object which creates the MDS URL and appends the query string parameterAjaxDelta=1. A request is created and downloaded asynchronously and only the "delta" is returned to the client and assembled and rendered using JavaScript.

How is the delta pages generated?

One of the key things in the MDS implementation is that SharePoint 2013 contains a new Page class - the DeltaPage. This page class is the key to generating the full pages or just the deltas. Fortunately the common pages such as WebPartPage, WikiEditPage, UnsecuredLayoutsPageBase, LayoutsPageBase etc are inheriting from this page. so most of the out-of-the box pages works and hopefully your custom application pages as well. This page class is quite smart it can handle when MDS is not enabled (ie default rendering) and when MDS is enabled. When MDS is enabled for the site this page is responsible for creating the delta response. The DeltaPage is also responsible for handling exceptions such as when the master page is different (another one or a new version). Every delta request sent using the asyncDeltaManager has a specific request header - the X-SharePoint header. This header contains information about the master page, the language and if the page is in read-write or read-only mode. This header is important since it contains the master page, the version of the master page etc and SharePoint uses this to determine whether to do a full reload (if the master page have changed) or not. So if you’re working on a master page, be prepared for full reloads of the page, at least the first time you request a page using the modified master page.

Enabling or disabling MDS on a site

 First of all what sites do have the MDS enabled by default? MDS is actually a SharePoint feature (Name; MDSFeature, Id: 87294C72-F260-42f3-A41B-981A2FFCE37A) which is Web scoped. Some of SharePoint 2013 Site Templates has this feature stapled (for instance Team, Community, Wiki, Projects, App and Blog sites). The feature is very straightforward - it toggles the EnableMinimalDownload property of the SPWeb object. So you can quite easily yourself turn it off using code (server side or CSOM!!) or PowerShell. Of course you also use the Site Settings > Site Features to turn it on or off as well.
The MDS is not enabled on publishing sites and is (as I understand it) not compatible with publishing sites, there are a couple of reasons for this, continue to read for more information

Requirements for MDS

Except enabling MDS it is required for the MDS to work there is one control that must be placed in the master page - the AjaxDelta control. It should be added to the head section of the master page. This control is responsible for a couple of things. If the page is a start/home page it will clear all controls and make sure that the page is loaded fully and registers the correct scripts.

The PageRenderMode control

 There is one control called PageRenderMode that can be used on a master page, web part page or page layout. This control has one property called RenderModeType which can have one of two possible values;Standard or MinimalDownload. If the control is placed on a page and have the property set to Standard – it will prohibit the page from being rendered using MDS. If the control is not present or if it’s property is set to MinimalDownload it can participate in MDS rendering. So this control can be used on specific (master)pages to prohibit MDS.

1
<SharePoint:PageRenderMode runat="server"
RenderModeType="MinimalDownload" />
A word of caution here, if you’re using the Design Manager to generate master pages, this control will automatically be inserted into the generated artifacts.
Another gotcha here is that even if you have the PageRenderMode control set to MinimalDownload whenever the publishing features are enabled and you have the ribbon on the page, specifically when using the PublishingRibbon in your master page, SharePoint will automagically inject, during runtime, the PageRenderMode control with the property set to Standard.

How does the delta response look like?

The response returned to the asyncDeltaManager has a specific format which very much resembles how ASP.NET UpdatePanels work. Each control that should be re-rendered on a page has it's HTML rendition and all these renditions are separated by a specific token which has the format:
-deltaboundary-<Guid>-
The Guid is unique (which is the purpose of guids :-) and the asyncDeltaManager retrieves the current token from the response header called deltaBoundary.
After all deltas, at the end of the response, is another encoded set of data This data tells what each delta boundary block is intended for and has the following format:
content length|type|id|data|
Type identifies what kind of delta data it is; controlDelta is a standard control, pageTitle the title of the page,pageRedirect a page redirection etc. There are about ten more different types. Id identifies the control identifier and data, is the data...
The asyncDeltaManager takes all this information and assembles the page, so it all looks fast and neat for the end users




In SharePoint master pages the AjaxDelta control is directly related to how the Minimal Download Strategy (MDS) knows which parts of the layout to refresh before the page is rendered. More specifically, anything not wrapped in an AjaxDelta control will not refresh between pages of a site with the MDS feature activated.
For example, if you have some dynamic page element, such as the page title, and it is not wrapped in an AjaxDelta control, every page on a site with MDS enabled will show the same title from page to page. This is because the first page routed through start.aspx causes MDS to cache everything on the page that isn’t wrapped in an AjaxDelta; every other page will show the same title. On the other hand, if it were wrapped in an AjaxDelta control, MDS would know it needs to render any differences from the previously loaded page.
<SharePoint:AjaxDelta ID="DeltaSearch" BlockElement="true" runat="server">
<asp:ContentPlaceHolder id="PlaceHolderSearchArea" runat="server">
<SearchWC:SearchBoxScriptWebPart
UseSiteCollectionSettings="true"
EmitStyleReference="false"
ShowQuerySuggestions="true"
ChromeType="None"
UseSharedSettings="true"
TryInplaceQuery="false"
ServerInitialRender="true"
runat="server" />
</asp:ContentPlaceHolder>
</SharePoint:AjaxDelta>

Each AjaxDelta control has a unique ID and an optional attribute BlockElement. When BlockElement is true the AjaxDelta wraps the control in an HTML <div>; otherwise it uses a <span>.
  1. The newly requested page has a different master page than the current page. This makes complete sense: requesting a page with a different master page than the current one means that the most basic structure of the page needs to be updated. These changes are too fundamental for MDS to handle. Keep your master page options as simple as possible to avoid this risk.
  2. The current master page has been updated since the last page load. Also makes sense for the same reason mentioned above.
  3. The current or requested page contains “non-compliant” content:
    • The page uses the ASP.NET 2.0 framework. The MDS server engine is only able to run its magic on pages running on ASP.NET 3.5 or later. Hopefully you aren’t deploying ASP.NET 2.0 pages to SharePoint anymore, but if you are, stop. No, seriously–stop.
    • The page uses CSS or Javascript files that aren’t registered with the MDS engine. For solutions deploying custom mark-up (in custom application pages), you can make MDS aware of CSS and JavaScript files by using the standard CssLink, CssRegistration, and ScriptLink server tags. If you aren’t in the habit of doing this already, see MSDN’s article on modifying SharePoint content for MDS for more info.
    • The page contains “illegal” HTMLAt the very least both <script/> and <style/> tags are considered illegal; as mentioned above, use the server-side CssLink, CssRegistration, and ScriptLink tags instead. “Illegal” HTML may cover other conditions that might prevent the MDS engine from correctly parsing the page. Avoid unclosed block-level tags, missing <head/> or <body/> sections, etc.
  4. The current or requested page contains “non-compliant” controls:
    • The control is not in the MDS engine whitelist. “Whitelisting” is achieved by registering resources with the SPPageContentManager class, using the RegisterClientScriptBlock(),
      RegisterClientScriptInclude()RegisterInlineStyle(),
      and similar methods. See 
      MSDN’s SPPageContentManager API reference for more details.
    • The control assembly is not marked as MDS-compliant. Assemblies that contain custom controls have to be marked with the Microsoft.SharePoint.WebControls.MdsCompliantAttribute to alert the server-side MDS engine that they have controls that support MDS. Reference MSDN’s article on modifying SharePoint content for MDS for examples of how this is done.
    • The control’s class doesn’t have the MDS attribute. Server-side control classes also have to be marked with the MdsCompliantAttribute to alert the MDS engine that they support it. This should only be done if all other compliance steps mentioned here have been taken.
As a developer of full-trust farm solutions for SharePoint 2013, you have a great deal of control over whether or not your custom code and controls cause MDS to fail.However, if you are developing SharePoint 2013 App Parts for SharePoint-Hosted or Provider-Hosted solutions, the MDS story for now is bleak. The control used by SharePoint to render embedded App Parts on a page causes MDS to fail: Every. Single. Time. Not only does it fail, but the failure appears to be caused by SharePoint’s internals; there’s no way to resolve this by making App Parts MDS compliant. We dug into the SharePoint 2013 source using .NET Reflector and discovered that the SPAppIFrame control used to render App Parts emits a raw <script/> tag using theHtmlTextWriter class. This makes the SPAppIFrame control non-MDS compliant  this doesn’t leave many options for Hosted Solution developers for now:
  1. Use Apps instead of App Parts. There’s significant business value in the capability of adding custom, configurable app logic (i.e. web parts) to SharePoint pages. But if you can architect your solution to keep your custom logic confined to its own pages, you have the opportunity to introduce your own “proper”(read: non-retrofit) SPA behavior by using Angular, Backbone, or another SPA framework within your app. Developing your own SPA logic reclaims significant control of application flow and performance; don’t discount this option just because “the client expects web parts.”
  2. Avoid routing requests to non-MDS-compliant pages through start.aspx. For example, if your site home page contains custom App Parts, any links to that page from your top navigation, quick links, wiki pages, and external sites should route directly to the <site>/SitePages/Home.aspx URL instead of using <site>/, which will cause it to route through start.aspx. Consider validating all of your navigation links, and any that contain start.aspx should be replaced if the link target includes a custom App Part.
  3. Disable the MDS feature on your SharePoint web. MDS is packaged as a feature on your SharePoint web, and it’s easy to disable. This is “the nuclear option”, but for sites with significant customizations on multiple pages, the overall performance may very well improve with MDS disabled.
Hope this will help you guy’s cheersJ




2 comments:

  1. Great Post..Appreciate your efforts in putting this together.. lot of blogs and articles on the same but no where i found it is composed completely..

    -PCM

    ReplyDelete
  2. Timely suggestions , Just to add my thoughts , you are interested in a a form , I filled out and esigned a sample version here http://goo.gl/NaIO4J.

    ReplyDelete