SharePoint Online のナビゲーション オプション

重要:  この記事は機械翻訳されています。機械翻訳についての「免責事項」をお読みください。この記事の英語版を参照するには、ここをクリックしてください。

この記事では、構造ナビゲーションや検索型ナビゲーションを使用して、SharePoint Online のページ読み込み時間を短縮する方法について説明します。

構造ナビゲーションの構築に必要なグローバル ナビゲーションとクエリのために、SharePoint Online のページの読み込み時間が長くなることがあります。 これは、各クエリが SQL Server に別々の要求を送信するためです。 運用しているサイトやサブサイトのそれぞれについて、さらに多くの要求が SQL Server に送信されます。 この問題は、マスター ページにも影響を及ぼします。 つまり、グローバル ナビゲーションにも影響を与えます。

SharePoint サイトによっては、大規模で複雑な構造を必要とします。 クエリ結果を利用する既定の構造ナビゲーションを使用すると、複数のサイト レイヤーのために、ページの読み込み時間が遅くなる場合があります。 サブサイトの各レイヤーでも、別のクエリが作成されます。

SharePoint のナビゲーション オプションには、既定の 2 つの主要なオプションの他に、3 番目のオプションとしてカスタムの検索型アプローチがあります。 いずれのオプションにも、次の表に示したような長所と短所があります。

構造ナビゲーション

管理ナビゲーション

検索型ナビゲーション

長所:

  • 構成しやすい

  • セキュリティ トリミングされる

  • サイトが追加されると自動的に更新される

長所:

  • メンテナンスしやすい

長所:

  • セキュリティ トリミングされる

  • サイトが追加されると自動的に更新される

  • 読み込み時間が短く、ナビゲーション構造がローカルにキャッシュされる

短所:

  • 複雑なサイト構造ではパフォーマンスが低いことがある

短所:

  • サイト構造を反映するように自動的に更新されない

短所:

  • サイトの順序を簡単に変更できない

  • マスター ページのカスタマイズが必要 (技術的スキルが必要)

多数のサブサイトを含むサイトがあって、構造ナビゲーションを使用している場合は、ページの読み込み時間が大幅に遅くなることがあります。 サイトに最適なオプションは、サイトの要件と技術的な能力によって異なります。 カスタムのマスター ページの使用に慣れていて、SharePoint Online の既定のマスター ページで発生する可能性がある変更のメンテナンスに対応する能力が組織にある場合は、検索型オプションのユーザー エクスペリエンスが最適と言えます。 既定の構造ナビゲーションと検索の中間的なシンプルなナビゲーションを求める場合は、管理ナビゲーションが最適なオプションです。 管理ナビゲーション オプションは、設定によってメンテナンスを行うことができ、コードのカスタマイズ ファイルを必要とせず、既定の構造ナビゲーションよりもはるかに高速です。

もう 1 つのアプローチは、既存のサイトを再構成して、必要なナビゲーション アイテムとサブサイトの数を減らすことです。 サイト構造とナビゲーションが複雑すぎない限り、構造ナビゲーションはパフォーマンスが高いためです。

この記事では、サンプルのサイト コレクションを用いて各種のアプローチを比較します。 サンプルのサイト コレクションには 11 個のサブサイトが含まれ、各サブサイトには 4 個以上の追加のサブサイトがあります。

サイトとサブ サイトを示すスクリーンショット

SharePoint Online で構造ナビゲーションを使用する

そのまま使用できる既定のナビゲーションで、ほとんどの状況において最も簡単かつ適切な対応方法です。 複数のサブサイトや階層の深いサブサイトによる複雑な構造が存在しない限り、構造ナビゲーションはパフォーマンスが高くなります。 この方法の主な利点としては、セキュリティ トリミングされること、新しいサイトが追加されると自動的に更新されること、マスター ページのカスタマイズが不要であることが挙げられます。 非技術系ユーザーでも、アイテムの追加、アイテムの非表示設定、ナビゲーションの管理を設定ページから簡単に行うことができます。

SharePoint Online で構造ナビゲーションを有効にする

構造ナビゲーションを使用していて、[サブサイトを表示する] オプションが有効になっている、標準の SharePoint Online ソリューションのパフォーマンスについて説明します。 次に示しているのは、[サイトの設定]、[ナビゲーション] の順に移動したページにある設定画面のスクリーン ショットです。

サブサイトを示すスクリーンショット

SharePoint Online の構造ナビゲーションのパフォーマンスを分析する

SharePoint のページのパフォーマンスを分析するには、Internet Explorer の F12 開発者ツールの [ネットワーク] タブを使います。

F12 開発ツールの [ネットワーク] タブを示すスクリーンショット

[ネットワーク] タブで、読み込み中の .aspx ページをクリックして、[詳細] タブをクリックします。

[詳細] タブを示すスクリーンショット

[応答ヘッダー] をクリックします。

[詳細] タブのスクリーンショット

SharePoint は、有益な診断情報を応答ヘッダーに含めて返します。 最も役立つ診断情報の 1 つは SPRequestDuration    で、これはサーバー上で要求の処理にかかった時間の値 (ミリ秒単位) です。

次のスクリーン ショットでは、構造ナビゲーションの [サブサイトを表示する] がオフになっています。 これは、グローバル ナビゲーションにサイト コレクションのリンクのみがあることを意味します。

要求の実行時間として読み込み時間を示すスクリーンショット

SPRequestDuration    キーの値は 245 ミリ秒です。 これは、要求を返すのにかかった時間を表しています。 サイトにあるナビゲーション アイテムは 1 つのみであるため、これは負荷の高いナビゲーションがない場合の SharePoint Online のパフォーマンスを表す適切なベンチマークと言えます。 次のスクリーン ショットは、サブサイトの算入でこのキーの値にどのような影響があるかを示しています。

要求の実行時間 2,502 ms を示すスクリーンショット

サブサイトの追加で、ページ要求が返されるまでの時間が大きく増加しています。

通常の構造ナビゲーションを使用する利点は、順序の整理、サイトの非表示設定、ページの追加を簡単に行うことができること、結果をセキュリティ トリミングできること、SharePoint Online でサポートされているマスター ページに手を加えて異なるファイルを作成しないで済むことです。 サイトの構成に注意を払って、サイト コレクションに含まれるサブサイトの数を最小限に抑えれば、構造ナビゲーションはパフォーマンスが高くなります。

SharePoint Online で管理ナビゲーションと管理されたメタデータを使用する

管理ナビゲーションは、構造ナビゲーションと同じ種類の機能を再作成するために使用できる、もう 1 つの既定のオプションです。

管理されたメタデータを使用する利点は、クエリ結果を使用してサイト ナビゲーションを構築する場合よりも、はるかに高速にデータを取得できることです。 非常に高速にはなりますが、結果をセキュリティ トリミングする方法がないため、ユーザーに特定のサイトへのアクセス権がない場合でもリンクはそのまま表示され、エラー メッセージが表示されることになります。

管理ナビゲーションと結果を実装する方法   

いくつかの記事 TechNet の管理ナビゲーション] の方法の詳細など、 SharePoint Server 2013 で管理ナビゲーションの概要を参照してください。

管理ナビゲーションを実装するには、用語ストア管理者の権限が必要です。 サイト コレクションの構造と一致する URL に対する用語を設定すると、管理ナビゲーションを構造ナビゲーションの代わりに使用できるようになります。 次に例を示します。

Subsite1 サンプルのスクリーンショット

次の例は、管理ナビゲーションを使用した複雑なナビゲーションのパフォーマンスを示しています。

SPRequestDuration のサンプルのスクリーンショット

常に管理ナビゲーションを使用すると、クエリ結果を用いた構造ナビゲーションのアプローチと比較してパフォーマンスが向上します。

検索型のクライアント側スクリプトを使用する

検索を使用すると、継続的クロールによってバックグラウンドで作成されるインデックスを利用できます。 つまり、負荷の高いコンテンツのクエリは発生しないことになります。 検索結果は検索インデックスから取得され、結果はセキュリティ トリミングされます。 これは、通常のコンテンツ クエリを使用するよりも高速です。 構造ナビゲーションで検索を使用すると、特に複雑なサイト構造がある場合に、ページの読み込み時間が大幅に短縮されます。 この方法が管理ナビゲーションよりも優れている点は、セキュリティ トリミングを利用できることです。

この方法には、カスタムのマスター ページを作成して、既定のナビゲーション コードをカスタム HTML に置き換える作業が含まれます。 ファイル seattle.html にあるナビゲーション コードを置き換えるには、次の手順に従います。

この例では、seattle.html ファイルを開いて、要素 id="DeltaTopNavigation" 全体をカスタム HTML コードで置き換えます。

例: マスター ページの既定のナビゲーションのコードを置換するには

  1. [サイトの設定] ページに移動します。

  2. [マスター ページ] をクリックして、マスター ページ ギャラリーを開きます。

  3. ここからライブラリ内を移動して、ファイル seattle.master をダウンロードできます。

  4. テキスト エディターでコードを編集して、次のスクリーン ショットに示したコード ブロックを削除します。

    削除する DeltaTopNavigation コードのスクリーンショット
  5. <SharePoint:AjaxDelta id=”DeltaTopNavigation”> タグと <\SharePoint:AjaxDelta> タグの間のコードを削除して、次のスニペットに置き換えます。

    <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">
                    <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 }">
                        <span aria-haspopup="true" class="additional-background ms-navedit-flyoutArrow dynamic-children">
                            <span class="menu-item-text" data-bind="text: item.Title">
                            </span>
                        </span>
                    </a>
                    <ul id="menu" data-bind="foreach: children; visible: children.length>0" class="dynamic" >
                        <li class="dynamic">
                            <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>
                </li>
            </ul>
        </div>
    </div>
  6. 読み込みの URL を置換する、サイト コレクションの読み込みイメージへのリンクで、最初にアンカー タグの画像します。変更を行った後、ファイルの名前を変更しをマスター ページ ギャラリーにアップロードします。これには、新しい .master ファイルが生成されます。

  7. この HTML は、JavaScript コードから返された検索結果によって入力される基本的なマークアップです。 次のコードを編集して、次のスニペットで示すように var root = “site collection URL の値を変更する必要があります。

    var root = “https://spperformance.sharepoint.com/sites/NavigationBySearch”;

    JavaScript ファイルの全体は次のようになります。

    //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 firefix 
        // 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.dynamic-children").mouseover(function () {
            var position = $(this).position();
            $(this).find("ul").css({ width: 125, left: position.left + 10, top: 50 });
    
        })
            .mouseout(function () {
                $(this).find("ul").css({ width: 0, left: -99999, top: 0 });
            });
    }
    
    _spBodyOnLoadFunctionNames.push("InitCustomNav");
    

    作成viewModelオブジェクトがあり、そのオブジェクトのloadNavigationNodes()機能、jQuery $(document).ready関数では、上記のコードを要約するには、と呼ばれます。この関数は、以前に作成したナビゲーション階層のクライアントのブラウザーの HTML5 のローカル ストレージに保存されているか、読み込みます。 または関数queryRemoteInterface()を呼び出します。

    QueryRemoteInterface()では、スクリプトで定義したクエリ パラメーターを使用して、 getRequest()関数を使用して、出席依頼を作成し、サーバーからデータを返します。このデータは、基本的にさまざまなプロパティを使用してデータ転送オブジェクトで表現されたサイト コレクション内のすべてのサイトの配列です。使用するための目に見えるプロパティを作成するのには Knockout.js を使用する定義済みのSPO.Models.NavigationNodeオブジェクトには、このデータはデータを以前のバージョンは定義した HTML に値を連結して解析します。結果の配列にオブジェクトが出力されます。次の配列がノックアウトを使用して JSON に解析し、将来のページの読み込みのパフォーマンスを向上のためのローカル ブラウザーの記憶域に保存します。

  8. 次に、結果はself.nodes配列に割り当てられたし、配列のself.heirarchyに出力を割り当てる linq.js を使用してオブジェクトから階層が組み込まれています。この配列は、HTML にバインドされているオブジェクトです。自己のオブジェクトをko.applyBinding()関数に渡すかどうかによってtoggleView()関数では、これです。これは、次の HTML にバインドする階層配列 [によりします。

    <div data-bind=”foreach: hierarchy” class=”noindex ms-core-listMenu-horizontalBox”>

    最後に、 mouseenterとmouseexitのイベント ハンドラーは、 addEventsToElements()関数では、これは、サブサイトのドロップダウン メニューを処理する最上位レベルのナビゲーションに追加されます。

    ナビゲーションの結果は、次のスクリーン ショットで確認できます。

    ナビゲーション結果のスクリーンショット

    この複雑なナビゲーションの例では、ローカル キャッシュがない新しいページの読み込みにおいて、管理ナビゲーションのアプローチと同様の結果を得るためにサーバーで費やされた時間が、ベンチマークの構造ナビゲーションよりも短縮されたことを示しています。

    SPRequestDuration 301 のスクリーンショット

    このアプローチの主な利点の 1 つは、ユーザーが次回ページを読み込むときのために、HTML5 のローカル記憶域を使用してナビゲーションがローカルに格納されることです。

構造ナビゲーション用に検索 API を使用すると、パフォーマンスが大きく向上します。ただし、この機能の実行とカスタマイズにはある程度の技術的な能力が必要になります。 実装の例では、サイトは既定の構造ナビゲーションと同じ順序 (アルファベット順) に並べられています。 これとは異なる順序にする必要がある場合は、開発とメンテナンスがより複雑になります。 また、このアプローチでは、サポートされているマスター ページに手を加えて異なるファイルを作成する必要もあります。 カスタムのマスター ページのメンテナンスを行わないと、Microsoft がマスター ページに提供する更新と改善がサイトに適用されないことになります。

上記のコードには、次の依存関係があります。

LinqJS の現在のバージョンでは、上記のコードで使用される ByHierarchy 方法が含まれていないと、ナビゲーション コードが解除されます。これを修正するのには、行の前に Linq.js ファイルに次の方法を追加"平展開: 機能 ()"。

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);
         })
     });
 },

注: 機械翻訳についての免責事項: この記事の翻訳はコンピューター システムによって行われており、人間の手は加えられていません。マイクロソフトでは、英語を話さないユーザーがマイクロソフトの製品、サービス、テクノロジに関するコンテンツを理解するのに役立てるため、こうした機械翻訳を提供しています。記事は機械翻訳されているため、用語、構文、文法などに誤りがある場合があります。

スキルを磨く
トレーニングの探索
新機能を最初に入手
Office Insider に参加する

この情報は役に立ちましたか?

ご意見をいただきありがとうございます。

フィードバックをお寄せいただき、ありがとうございます。Office サポートの担当者におつなぎいたします。

×