Share via


Navigeringsalternativ för SharePoint

I den här artikeln beskrivs webbplatser för navigeringsalternativ med SharePoint-publicering aktiverat i SharePoint. Valet och konfigurationen av navigeringen påverkar avsevärt prestanda och skalbarhet för webbplatser i SharePoint. SharePoint-publiceringswebbplatsmallen bör endast användas om det behövs för en centraliserad portal och publiceringsfunktionen bör endast aktiveras på specifika webbplatser och endast när det behövs eftersom den kan påverka prestanda när den används felaktigt.

Obs!

Om du använder moderna SharePoint-navigeringsalternativ som megameny, sammanhängande navigering eller navnavigering gäller inte den här artikeln för din webbplats. Moderna SharePoint-webbplatsarkitekturer utnyttjar en mer utplattad platshierarki och en hub-and-spoke-modell. Detta gör att många scenarier kan uppnås som INTE kräver användning av SharePoint-publiceringsfunktionen.

Översikt över navigeringsalternativ

Konfigurationen av navigeringsprovidern kan avsevärt påverka prestanda för hela webbplatsen, och du måste noga överväga att välja en navigeringsprovider och konfiguration som skalar effektivt för kraven på en SharePoint-webbplats. Det finns två färdiga navigeringsproviders samt anpassade navigeringsimplementeringar.

Det första alternativet , Strukturell navigering, är det rekommenderade navigeringsalternativet i SharePoint för klassiska SharePoint-webbplatser , om du aktiverar strukturell navigeringscachelagring för webbplatsen. Den här navigeringsprovidern visar navigeringsobjekten under den aktuella webbplatsen och eventuellt den aktuella webbplatsen och dess syskon. Den innehåller ytterligare funktioner som säkerhetstrimning och platsstrukturuppräkning. Om cachelagring är inaktiverat påverkar detta prestanda och skalbarhet negativt och kan vara föremål för begränsningar.

Det andra alternativet, Hanterad navigering (metadata), representerar navigeringsobjekt med hjälp av en termuppsättning för hanterade metadata. Vi rekommenderar att säkerhetstrimning inaktiveras om det inte krävs. Säkerhetstrimning är aktiverat som en säker som standardinställning för den här navigeringsprovidern. Många webbplatser kräver dock inte att säkerheten trimmas eftersom navigeringselement ofta är konsekventa för alla användare av webbplatsen. Med den rekommenderade konfigurationen för att inaktivera säkerhetstrimning kräver den här navigeringsprovidern inte uppräkning av platsstruktur och är mycket skalbar med acceptabel prestandapåverkan.

Förutom de färdiga navigeringsleverantörerna har många kunder implementerat alternativa anpassade navigeringsimplementeringar. Se skript på Search klientsidan i den här artikeln.

Alternativ för för- och nackdelar med SharePoint-navigering

I följande tabell sammanfattas för- och nackdelarna med varje alternativ.

Strukturell navigering Hanterad navigering Search-driven navigering Anpassad navigeringsprovider
Proffsen:

Lätt att underhålla
Säkerhetstrimmad
Uppdateras automatiskt inom 24 timmar när innehållet ändras
Proffsen:

Lätt att underhålla
Proffsen:

Säkerhetstrimmad
Uppdateringar automatiskt när webbplatser läggs till
Snabb inläsningstid och lokalt cachelagrad navigeringsstruktur
Proffsen:

Bredare val av tillgängliga alternativ
Snabb inläsning när cachelagring används korrekt
Många alternativ fungerar bra med dynamisk siddesign
Nackdelar:

Påverkar prestanda om cachelagring är inaktiverat
Begränsas
Nackdelar:

Uppdateras inte automatiskt för att återspegla webbplatsstrukturen
Påverkar prestanda om säkerhetstrimning är aktiverat eller när navigeringsstrukturen är komplex
Nackdelar:

Ingen möjlighet att enkelt beställa webbplatser
Kräver anpassning av huvudsidan (tekniska kunskaper krävs)
Nackdelar:

Anpassad utveckling krävs
Extern datakälla/lagrad cache krävs, t.ex. Azure

Det lämpligaste alternativet för din webbplats beror på dina webbplatskrav och på din tekniska kapacitet. Om du vill ha en lättkonfigurerad navigeringsprovider som automatiskt uppdateras när innehållet ändras är strukturell navigering med cachelagring aktiverat ett bra alternativ.

Obs!

Att tillämpa samma princip som moderna SharePoint-webbplatser genom att förenkla den övergripande webbplatsstrukturen till en plattare, icke-hierarkisk struktur förbättrar prestanda och förenklar övergången till moderna SharePoint-webbplatser. Det innebär att i stället för att ha en enda webbplatssamling med hundratals webbplatser (underwebbplatser) är en bättre metod att ha många webbplatssamlingar med mycket få underwebbplatser (underwebbplatser).

Analysera navigeringsprestanda i SharePoint

Verktyget Siddiagnostik för SharePoint är ett webbläsartillägg för Microsoft Edge- och Chrome-webbläsare som analyserar både sidor i den moderna SharePoint-portalen och den klassiska publiceringswebbplatsen. Det här verktyget fungerar bara för SharePoint och kan inte användas på en SharePoint-systemsida.

Verktyget genererar en rapport för varje analyserad sida som visar hur sidan presterar mot en fördefinierad uppsättning regler och visar detaljerad information när resultaten för ett test faller utanför baslinjevärdet. SharePoint-administratörer och -designers kan använda verktyget för att felsöka prestandaproblem för att säkerställa att nya sidor optimeras före publiceringen.

SÄRSKILT SPRequestDuration är den tid det tar för SharePoint att bearbeta sidan. Tung navigering (som att inkludera sidor i navigeringen), komplexa platshierarkier och andra konfigurations- och topologialternativ kan alla dramatiskt bidra till längre varaktigheter.

Använda strukturell navigering i SharePoint

Det här är den färdiga navigeringen som används som standard och är den enklaste lösningen. Det kräver ingen anpassning och en icke-teknisk användare kan också enkelt lägga till objekt, dölja objekt och hantera navigeringen från inställningssidan. Vi rekommenderar att du aktiverar cachelagring, annars blir prestandan dyrare.

Så här implementerar du strukturell navigeringscachelagring

Under Webbplatsinställningar>Look and Feel Navigation (Look and Feel>Navigation) kan du kontrollera om strukturell navigering har valts för antingen global navigering eller aktuell navigering. Om du väljer Visa sidor påverkas prestanda negativt.

Strukturell navigering med Visa underwebbplatser valt.

Cachelagring kan aktiveras eller inaktiveras på webbplatssamlingsnivå och på webbplatsnivå och aktiveras för båda som standard. Om du vill aktivera på webbplatssamlingsnivå markerar du kryssrutan Aktivera cachelagring under Webbplatsinställningar>Webbplatssamling administration>webbplatssamling navigering.

Aktivera cachelagring på webbplatssamlingsnivå.

Om du vill aktivera på platsnivå markerar du kryssrutan Aktivera cachelagring underNavigering för webbplatsinställningar>.

Aktivera cachelagring på platsnivå.

Använda hanterad navigering och metadata i SharePoint

Hanterad navigering är ett annat out-of-the-box-alternativ som du kan använda för att återskapa de flesta av samma funktioner som strukturell navigering. Hanterade metadata kan konfigureras så att säkerhetstrimning är aktiverat eller inaktiverat. När den konfigureras med säkerhetstrimning inaktiverad är hanterad navigering ganska effektiv eftersom den läser in alla navigeringslänkar med ett konstant antal serveranrop. Om du aktiverar säkerhetstrimning negeras dock några av prestandafördelarna med hanterad navigering.

Om du behöver aktivera säkerhetstrimning rekommenderar vi att du:

  • Uppdatera alla egna URL-länkar till enkla länkar
  • Lägga till nödvändiga säkerhetstrimningsnoder som egna URL:er
  • Begränsa antalet navigeringsobjekt till högst 100 och högst tre nivåer

Många webbplatser kräver inte säkerhetstrimning eftersom navigeringsstrukturen ofta är konsekvent för alla användare av webbplatsen. Om säkerhetstrimning är inaktiverat och en länk läggs till i navigeringen som inte alla användare har åtkomst till visas länken fortfarande, men leder till ett meddelande om nekad åtkomst. Det finns ingen risk för oavsiktlig åtkomst till innehållet.

Så här implementerar du hanterad navigering och resultat

Det finns flera artiklar på Microsoft Learn om information om hanterad navigering. Se till exempel Översikt över hanterad navigering i SharePoint Server.

För att implementera hanterad navigering konfigurerar du termer med URL:er som motsvarar webbplatsens navigeringsstruktur. Hanterad navigering kan till och med kureras manuellt för att ersätta strukturell navigering i många fall. Till exempel:

SharePoint-webbplatsstruktur.)

Använda skript på Search klientsidan

En vanlig klass med anpassade navigeringsimplementeringar omfattar klient renderade designmönster som lagrar en lokal cache med navigeringsnoder.

Dessa navigeringsproviders har några viktiga fördelar:

  • De fungerar vanligtvis bra med responsiva siddesigner.
  • De är extremt skalbara och högpresterande eftersom de kan renderas utan resurskostnad (och uppdateras i bakgrunden efter en timeout).
  • Dessa navigeringsproviders kan hämta navigeringsdata med hjälp av olika strategier, allt från enkla statiska konfigurationer till olika dynamiska dataleverantörer.

Ett exempel på en dataleverantör är att använda en Search-driven navigering, vilket ger flexibilitet för att räkna upp navigeringsnoder och hantera säkerhetstrimning effektivt.

Det finns andra populära alternativ för att skapa anpassade navigeringsproviders. Mer information om hur du skapar en anpassad navigeringsprovider finns i Navigeringslösningar för SharePoint-portaler .

Med hjälp av sökning kan du utnyttja de index som är uppbyggda i bakgrunden med kontinuerlig crawlning. Sökresultaten hämtas från sökindexet och resultaten är säkerhetstrimmade. Detta är vanligtvis snabbare än färdiga navigeringsproviders när säkerhetstrimning krävs. Om du använder sökning efter strukturell navigering, särskilt om du har en komplex webbplatsstruktur, påskyndas sidinläsningstiden avsevärt. Den största fördelen med detta jämfört med hanterad navigering är att du drar nytta av säkerhetstrimning.

Den här metoden innebär att skapa en anpassad huvudsida och ersätta den färdiga navigeringskoden med anpassad HTML. Följ den här proceduren som beskrivs i följande exempel för att ersätta navigeringskoden i filen seattle.html. I det här exemplet öppnar seattle.html du filen och ersätter hela elementet id="DeltaTopNavigation" med anpassad HTML-kod.

Exempel: Ersätt den färdiga navigeringskoden på en huvudsida

  1. Gå till sidan Webbplatsinställningar.
  2. Öppna galleriet för huvudsidor genom att klicka på Huvudsidor.
  3. Härifrån kan du navigera genom biblioteket och ladda ned filen seattle.master.
  4. Redigera koden med hjälp av en textredigerare och ta bort kodblocket på följande skärmbild.
    Ta bort det kodblock som visas.
  5. Ta bort koden mellan taggarna <SharePoint:AjaxDelta id="DeltaTopNavigation"> och <\SharePoint:AjaxDelta> och ersätt den med följande kodfragment:
<div id="loading">
  <!--Replace with path to loading image.-->
  <div style="background-image: url(''); height: 22px; width: 22px; ">
  </div>
</div>
<!-- Main Content-->
<div id="navContainer" style="display:none">
    <div data-bind="foreach: hierarchy" class="noindex ms-core-listMenu-horizontalBox">
        <a class="dynamic menu-item ms-core-listMenu-item ms-displayInline ms-navedit-linkNode" data-bind="attr: { href: item.Url, title: item.Title }">
            <span class="menu-item-text" data-bind="text: item.Title">
            </span>
        </a>
        <ul id="menu" data-bind="foreach: $data.children" style="padding-left:20px">
            <li class="static dynamic-children level1">
                <a class="static dynamic-children menu-item ms-core-listMenu-item ms-displayInline ms-navedit-linkNode" data-bind="attr: { href: item.Url, title: item.Title }">

                 <!-- ko if: children.length > 0-->
                    <span aria-haspopup="true" class="additional-background ms-navedit-flyoutArrow dynamic-children">
                        <span class="menu-item-text" data-bind="text: item.Title">
                        </span>
                    </span>
                <!-- /ko -->
                <!-- ko if: children.length == 0-->
                    <span aria-haspopup="true" class="ms-navedit-flyoutArrow dynamic-children">
                        <span class="menu-item-text" data-bind="text: item.Title">
                        </span>
                    </span>
                <!-- /ko -->
                </a>

                <!-- ko if: children.length > 0-->
                <ul id="menu"  data-bind="foreach: children;" class="dynamic  level2" >
                    <li class="dynamic level2">
                        <a class="dynamic menu-item ms-core-listMenu-item ms-displayInline  ms-navedit-linkNode" data-bind="attr: { href: item.Url, title: item.Title }">

          <!-- ko if: children.length > 0-->
          <span aria-haspopup="true" class="additional-background ms-navedit-flyoutArrow dynamic-children">
           <span class="menu-item-text" data-bind="text: item.Title">
           </span>
          </span>
           <!-- /ko -->
          <!-- ko if: children.length == 0-->
          <span aria-haspopup="true" class="ms-navedit-flyoutArrow dynamic-children">
           <span class="menu-item-text" data-bind="text: item.Title">
           </span>
          </span>
          <!-- /ko -->
                        </a>
          <!-- ko if: children.length > 0-->
         <ul id="menu" data-bind="foreach: children;" class="dynamic level3" >
          <li class="dynamic level3">
           <a class="dynamic menu-item ms-core-listMenu-item ms-displayInline ms-navedit-linkNode" data-bind="attr: { href: item.Url, title: item.Title }">
            <span class="menu-item-text" data-bind="text: item.Title">
            </span>
           </a>
          </li>
         </ul>
           <!-- /ko -->
                    </li>
                </ul>
                <!-- /ko -->
            </li>
        </ul>
    </div>
</div>

6. Ersätt URL:en i fästpunktstaggen för inläsning av bild i början med en länk till en inläsningsbild i webbplatssamlingen. När du har gjort ändringarna byter du namn på filen och laddar sedan upp den till galleriet för huvudsidor. Detta genererar en ny .master-fil.
7. Den här HTML-koden är den grundläggande koden som fylls i av sökresultaten som returneras från JavaScript-koden. Du måste redigera koden för att ändra värdet för var root = "webbplatssamlings-URL" enligt följande kodfragment:
var root = "https://spperformance.sharepoint.com/sites/NavigationBySearch";

8. Resultaten tilldelas till matrisen self.nodes och en hierarki är inbyggd i objekten med hjälp av linq.js tilldela utdata till en matris self.hierarchy. Den här matrisen är det objekt som är bundet till HTML. Detta görs i funktionen toggleView() genom att skicka självobjektet till funktionen ko.applyBinding().
Detta gör att hierarkimatrisen binds till följande HTML:
<div data-bind="foreach: hierarchy" class="noindex ms-core-listMenu-horizontalBox">

Händelsehanterarna för och mouseexit läggs till i navigeringen på den översta nivån för mouseenter att hantera de nedrullningsbara underwebbplatsmenyerna som görs i addEventsToElements() funktionen.

I vårt komplexa navigeringsexempel visar en ny sidinläsning utan lokal cachelagring att den tid som lagts på servern har skurits ned från benchmark-strukturell navigering för att få ett liknande resultat som den hanterade navigeringsmetoden.

Om JavaScript-filen...

Obs!

Om du använder anpassad JavaScript kontrollerar du att offentligt CDN är aktiverat och att filen finns på en CDN-plats.

Hela JavaScript-filen är följande:

//Models and Namespaces
var SPOCustom = SPOCustom || {};
SPOCustom.Models = SPOCustom.Models || {}
SPOCustom.Models.NavigationNode = function () {

    this.Url = ko.observable("");
    this.Title = ko.observable("");
    this.Parent = ko.observable("");

};

var root = "https://spperformance.sharepoint.com/sites/NavigationBySearch";
var baseUrl = root + "/_api/search/query?querytext=";
var query = baseUrl + "'contentClass=\"STS_Web\"+path:" + root + "'&trimduplicates=false&rowlimit=300";

var baseRequest = {
    url: "",
    type: ""
};


//Parses a local object from JSON search result.
function getNavigationFromDto(dto) {
    var item = new SPOCustom.Models.NavigationNode();
    if (dto != undefined) {

        var webTemplate = getSearchResultsValue(dto.Cells.results, 'WebTemplate');

        if (webTemplate != "APP") {
            item.Title(getSearchResultsValue(dto.Cells.results, 'Title')); //Key = Title
            item.Url(getSearchResultsValue(dto.Cells.results, 'Path')); //Key = Path
            item.Parent(getSearchResultsValue(dto.Cells.results, 'ParentLink')); //Key = ParentLink
        }

    }
    return item;
}

function getSearchResultsValue(results, key) {

    for (i = 0; i < results.length; i++) {
        if (results[i].Key == key) {
            return results[i].Value;
        }
    }
    return null;
}

//Parse a local object from the serialized cache.
function getNavigationFromCache(dto) {
    var item = new SPOCustom.Models.NavigationNode();

    if (dto != undefined) {

        item.Title(dto.Title);
        item.Url(dto.Url);
        item.Parent(dto.Parent);
    }

    return item;
}

/* create a new OData request for JSON response */
function getRequest(endpoint) {
    var request = baseRequest;
    request.type = "GET";
    request.url = endpoint;
    request.headers = { ACCEPT: "application/json;odata=verbose" };
    return request;
};

/* Navigation Module*/
function NavigationViewModel() {
    "use strict";
    var self = this;
    self.nodes = ko.observableArray([]);
    self.hierarchy = ko.observableArray([]);;
    self.loadNavigatioNodes = function () {
        //Check local storage for cached navigation datasource.
        var fromStorage = localStorage["nodesCache"];
        if (false) {
            var cachedNodes = JSON.parse(localStorage["nodesCache"]);

            if (cachedNodes && timeStamp) {
                //Check for cache expiration. Currently set to 3 hrs.
                var now = new Date();
                var diff = now.getTime() - timeStamp;
                if (Math.round(diff / (1000 * 60 * 60)) < 3) {

                    //return from cache.
                    var cacheResults = [];
                    $.each(cachedNodes, function (i, item) {
                        var nodeitem = getNavigationFromCache(item, true);
                        cacheResults.push(nodeitem);
                    });

                    self.buildHierarchy(cacheResults);
                    self.toggleView();
                    addEventsToElements();
                    return;
                }
            }
        }
        //No cache hit, REST call required.
        self.queryRemoteInterface();
    };

    //Executes a REST call and builds the navigation hierarchy.
    self.queryRemoteInterface = function () {
        var oDataRequest = getRequest(query);
        $.ajax(oDataRequest).done(function (data) {
            var results = [];
            $.each(data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results, function (i, item) {

                if (i == 0) {
                    //Add root element.
                    var rootItem = new SPOCustom.Models.NavigationNode();
                    rootItem.Title("Root");
                    rootItem.Url(root);
                    rootItem.Parent(null);
                    results.push(rootItem);
                }
                var navItem = getNavigationFromDto(item);
                results.push(navItem);
            });
            //Add to local cache
            localStorage["nodesCache"] = ko.toJSON(results);

            localStorage["nodesCachedAt"] = new Date().getTime();
            self.nodes(results);
            if (self.nodes().length > 0) {
                var unsortedArray = self.nodes();
                var sortedArray = unsortedArray.sort(self.sortObjectsInArray);

                self.buildHierarchy(sortedArray);
                self.toggleView();
                addEventsToElements();
            }
        }).fail(function () {
            //Handle error here!!
            $("#loading").hide();
            $("#error").show();
        });
    };
    self.toggleView = function () {
        var navContainer = document.getElementById("navContainer");
        ko.applyBindings(self, navContainer);
        $("#loading").hide();
        $("#navContainer").show();

    };
    //Uses linq.js to build the navigation tree.
    self.buildHierarchy = function (enumerable) {
        self.hierarchy(Enumerable.From(enumerable).ByHierarchy(function (d) {
            return d.Parent() == null;
        }, function (parent, child) {
            if (parent.Url() == null || child.Parent() == null)
                return false;
            return parent.Url().toUpperCase() == child.Parent().toUpperCase();
        }).ToArray());

        self.sortChildren(self.hierarchy()[0]);
    };


    self.sortChildren = function (parent) {

        // sjip processing if no children
        if (!parent || !parent.children || parent.children.length === 0) {
            return;
        }

        parent.children = parent.children.sort(self.sortObjectsInArray2);

        for (var i = 0; i < parent.children.length; i++) {
            var elem = parent.children[i];

            if (elem.children && elem.children.length > 0) {
                self.sortChildren(elem);
            }
        }
    };

    // ByHierarchy method breaks the sorting in chrome and firefox
    // we need to resort  as ascending
    self.sortObjectsInArray2 = function (a, b) {
        if (a.item.Title() > b.item.Title())
            return 1;
        if (a.item.Title() < b.item.Title())
            return -1;
        return 0;
    };


    self.sortObjectsInArray = function (a, b) {
        if (a.Title() > b.Title())
            return -1;
        if (a.Title() < b.Title())
            return 1;
        return 0;
    }
}

//Loads the navigation on load and binds the event handlers for mouse interaction.
function InitCustomNav() {
    var viewModel = new NavigationViewModel();
    viewModel.loadNavigatioNodes();
}

function addEventsToElements() {
    //events.
      $("li.level1").mouseover(function () {
          var position = $(this).position();
          $(this).find("ul.level2").css({ width: 100, left: position.left + 10, top: 50 });
      })
   .mouseout(function () {
     $(this).find("ul.level2").css({  left: -99999, top: 0 });
   
    });
   
     $("li.level2").mouseover(function () {
          var position = $(this).position();
          console.log(JSON.stringify(position));
          $(this).find("ul.level3").css({ width: 100, left: position.left + 95, top:  position.top});
      })
   .mouseout(function () {
     $(this).find("ul.level3").css({  left: -99999, top: 0 });
    });
} _spBodyOnLoadFunctionNames.push("InitCustomNav");

För att sammanfatta koden som visas ovan i jQuery $(document).ready funktionen finns det en viewModel object skapad och sedan loadNavigationNodes() anropas funktionen för objektet. Den här funktionen läser antingen in den tidigare skapade navigeringshierarkin som lagras i html5-lokal lagring i klientwebbläsaren eller så anropas funktionen queryRemoteInterface().

QueryRemoteInterface() skapar en begäran med hjälp av getRequest() funktionen med frågeparametern som definierades tidigare i skriptet och returnerar sedan data från servern. Dessa data är i princip en matris med alla platser i webbplatssamlingen som representeras som dataöverföringsobjekt med olika egenskaper.

Dessa data parsas sedan i de tidigare definierade SPO.Models.NavigationNode objekten som används Knockout.js för att skapa observerbara egenskaper för användning av data som binder värdena till HTML-koden som vi definierade tidigare.

Objekten placeras sedan i en resultatmatris. Den här matrisen parsas i JSON med knockout och lagras i den lokala webbläsarlagringen för bättre prestanda vid framtida sidinläsningar.

Fördelar med den här metoden

En stor fördel med den här metoden är att navigeringen lagras lokalt för användaren nästa gång de läser in sidan med hjälp av HTML5-lokal lagring. Vi får stora prestandaförbättringar från att använda sök-API:et för strukturell navigering. Det krävs dock viss teknisk kapacitet för att köra och anpassa den här funktionen.

I exempelimplementeringen sorteras platserna på samma sätt som den färdiga strukturella navigeringen. alfabetisk ordning. Om du vill avvika från den här ordningen skulle det vara mer komplicerat att utveckla och underhålla. Den här metoden kräver också att du avviker från de huvudsidor som stöds. Om den anpassade huvudsidan inte upprätthålls går webbplatsen miste om uppdateringar och förbättringar som Microsoft gör på huvudsidorna.

Koden ovan har följande beroenden:

Den aktuella versionen av LinqJS innehåller inte metoden ByHierarchy som används i koden ovan och kommer att bryta navigeringskoden. Åtgärda detta genom att lägga till följande metod i filen Linq.js före raden Flatten: function ().

ByHierarchy: function(firstLevel, connectBy, orderBy, ascending, parent) {
     ascending = ascending == undefined ? true : ascending;
     var orderMethod = ascending == true ? 'OrderBy' : 'OrderByDescending';
     var source = this;
     firstLevel = Utils.CreateLambda(firstLevel);
     connectBy = Utils.CreateLambda(connectBy);
     orderBy = Utils.CreateLambda(orderBy);

     //Initiate or increase level
     var level = parent === undefined ? 1 : parent.level + 1;

    return new Enumerable(function() {
         var enumerator;
         var index = 0;

        var createLevel = function() {
                 var obj = {
                     item: enumerator.Current(),
                     level : level
                 };
                 obj.children = Enumerable.From(source).ByHierarchy(firstLevel, connectBy, orderBy, ascending, obj);
                 if (orderBy !== undefined) {
                     obj.children = obj.children[orderMethod](function(d) {
                         return orderBy(d.item); //unwrap the actual item for sort to work
                     });
                 }
                 obj.children = obj.children.ToArray();
                 Enumerable.From(obj.children).ForEach(function(child) {
                     child.getParent = function() {
                         return obj;
                     };
                 });
                 return obj;
             };

        return new IEnumerator(

        function() {
             enumerator = source.GetEnumerator();
         }, function() {
             while (enumerator.MoveNext()) {
                 var returnArr;
                 if (!parent) {
                     if (firstLevel(enumerator.Current(), index++)) {
                         return this.Yield(createLevel());
                     }

                } else {
                     if (connectBy(parent.item, enumerator.Current(), index++)) {
                         return this.Yield(createLevel());
                     }
                 }
             }
             return false;
         }, function() {
             Utils.Dispose(enumerator);
         })
     });
 },

Översikt över hanterad navigering i SharePoint Server

Strukturell cachelagring och prestanda för navigering