خيارات التنقل ل SharePoint

توضح هذه المقالة مواقع خيارات التنقل مع تمكين نشر SharePoint في SharePoint. يؤثر اختيار التنقل وتكوينه بشكل كبير على أداء المواقع في SharePoint وقابليتها للتوسع. يجب استخدام قالب موقع نشر SharePoint فقط إذا لزم الأمر لمدخل مركزي ويجب تمكين ميزة النشر على مواقع معينة فقط عند الحاجة لأنها يمكن أن تؤثر على الأداء عند استخدامها بشكل غير صحيح.

ملاحظة

إذا كنت تستخدم خيارات التنقل الحديثة في SharePoint مثل القائمة الضخمة أو التنقل المتتالي أو التنقل في المركز، فلن تنطبق هذه المقالة على موقعك. تستفيد بنيات مواقع SharePoint الحديثة من التسلسل الهرمي للموقع الأكثر تسوية ونموذجا محوريا. يسمح هذا بتحقيق العديد من السيناريوهات التي لا تتطلب استخدام ميزة نشر SharePoint.

نظرة عامة على خيارات التنقل

يمكن أن يؤثر تكوين موفر التنقل بشكل كبير على أداء الموقع بأكمله، ويجب مراعاة بعناية لاختيار موفر التنقل والتكوين الذي يتوسع بشكل فعال لمتطلبات موقع SharePoint. هناك اثنان من موفري التنقل الجاهزين، بالإضافة إلى تطبيقات التنقل المخصصة.

الخيار الأول، التنقل الهيكلي، هو خيار التنقل الموصى به في SharePoint لمواقع SharePoint الكلاسيكية، إذا قمت بتشغيل التخزين المؤقت للتنقل الهيكلي لموقعك. يعرض موفر التنقل هذا عناصر التنقل أسفل الموقع الحالي، واختياريا الموقع الحالي وأخواته. يوفر إمكانات إضافية مثل اقتطاع الأمان وتعداد بنية الموقع. إذا تم تعطيل التخزين المؤقت، فسيؤثر ذلك سلبا على الأداء وقابلية التوسع، وقد يكون عرضة للتقييد.

يمثل الخيار الثاني، التنقل المدار (بيانات التعريف)، عناصر التنقل باستخدام مجموعة مصطلحات بيانات التعريف المدارة. نوصي بتعطيل اقتطاع الأمان ما لم يكن مطلوبا. يتم تمكين اقتطاع الأمان كإعداد آمن بشكل افتراضي لموفر التنقل هذا؛ ومع ذلك، لا تتطلب العديد من المواقع حمل الاقتطاع الأمني لأن عناصر التنقل غالبا ما تكون متسقة لجميع مستخدمي الموقع. مع التكوين الموصى به لتعطيل اقتطاع الأمان، لا يتطلب موفر التنقل هذا تعداد بنية الموقع وهو قابل للتطوير بدرجة كبيرة مع تأثير أداء مقبول.

بالإضافة إلى موفري التنقل الجاهزين، نجح العديد من العملاء في تنفيذ تطبيقات التنقل المخصصة البديلة. راجع البرمجة النصية من جانب العميل المستندة إلى البحث في هذه المقالة.

إيجابيات وسلبيات خيارات التنقل في SharePoint

يلخص الجدول التالي إيجابيات وسلبيات كل خيار.

التنقل الهيكلي التنقل المدار التنقل المستند إلى البحث موفر التنقل المخصص
الايجابيات:

سهلة الصيانة
تم اقتطاع الأمان
يتم التحديث تلقائيا في غضون 24 ساعة عند تغيير المحتوى
الايجابيات:

سهلة الصيانة
الايجابيات:

تم اقتطاع الأمان
التحديثات تلقائيا أثناء إضافة المواقع
وقت التحميل السريع وبنية التنقل المخزنة مؤقتا محليا
الايجابيات:

خيارات أوسع متاحة
التحميل السريع عند استخدام التخزين المؤقت بشكل صحيح
تعمل العديد من الخيارات بشكل جيد مع تصميم الصفحة المتجاوب
سلبيات:

يؤثر على الأداء إذا تم تعطيل التخزين المؤقت
عرضة للتقييد
سلبيات:

لم يتم تحديثه تلقائيا ليعكس بنية الموقع
يؤثر على الأداء إذا تم تمكين اقتطاع الأمان أو عندما تكون بنية التنقل معقدة
سلبيات:

لا توجد إمكانية لطلب المواقع بسهولة
يتطلب تخصيص الصفحة الرئيسية (المهارات التقنية المطلوبة)
سلبيات:

التطوير المخصص مطلوب
هناك حاجة إلى مصدر البيانات الخارجي / ذاكرة التخزين المؤقت المخزنة، على سبيل المثال، Azure

يعتمد الخيار الأنسب لموقعك على متطلبات موقعك وعلى قدرتك التقنية. إذا كنت تريد موفر تنقل سهل التكوين يتم تحديثه تلقائيا عند تغيير المحتوى، فإن التنقل الهيكلي مع تمكين التخزين المؤقت هو خيار جيد.

ملاحظة

يؤدي تطبيق نفس المبدأ مثل مواقع SharePoint الحديثة من خلال تبسيط بنية الموقع الإجمالية إلى بنية أكثر بساطة وغير هرمية إلى تحسين الأداء وتبسيط الانتقال إلى مواقع SharePoint الحديثة. ما يعنيه هذا هو أنه بدلا من وجود مجموعة مواقع مشتركة واحدة تحتوي على مئات المواقع (المستندات الفرعية)، فإن النهج الأفضل هو أن يكون لديك العديد من مجموعات المواقع المشتركة مع عدد قليل جدا من المواقع الفرعية (المستندات الفرعية).

تحليل أداء التنقل في SharePoint

أداة تشخيص الصفحة ل SharePoint هي ملحق مستعرض لمستعرضات Microsoft Edge وChrome التي تحلل كلا من مدخل SharePoint الحديث وصفحات موقع النشر الكلاسيكية. تعمل هذه الأداة فقط مع SharePoint، ولا يمكن استخدامها على صفحة نظام SharePoint.

تنشئ الأداة تقريرا لكل صفحة تم تحليلها توضح كيفية أداء الصفحة مقابل مجموعة محددة مسبقا من القواعد وتعرض معلومات مفصلة عندما تقع نتائج الاختبار خارج قيمة الأساس. يمكن لمسؤولي SharePoint ومصمميها استخدام الأداة لاستكشاف مشكلات الأداء وإصلاحها لضمان تحسين الصفحات الجديدة قبل النشر.

SPRequestDuration على وجه الخصوص هو الوقت الذي يستغرقه SharePoint لمعالجة الصفحة. يمكن أن يساهم التنقل الثقيل (مثل تضمين الصفحات في التنقل) والتسلسلات الهرمية المعقدة للموقع وخيارات التكوين والطوبولوجيا الأخرى بشكل كبير في فترات أطول.

استخدام التنقل الهيكلي في SharePoint

هذا هو التنقل الجاهز المستخدم افتراضيا وهو الحل الأكثر وضوحا. لا يتطلب أي تخصيص ويمكن للمستخدم غير التقني أيضا إضافة العناصر وإخفاء العناصر وإدارة التنقل من صفحة الإعدادات بسهولة. نوصي بتمكين التخزين المؤقت، وإلا فهناك مفاضلة أداء باهظة الثمن.

كيفية تنفيذ التخزين المؤقت للتنقل الهيكلي

ضمن إعدادات> الموقعالشكل والتنقل،> يمكنك التحقق من صحة تحديد التنقل الهيكلي للتنقل العمومي أو التنقل الحالي. سيكون لتحديد إظهار الصفحات تأثير سلبي على الأداء.

التنقل الهيكلي مع تحديد إظهار المواقع الفرعية.

يمكن تمكين التخزين المؤقت أو تعطيله على مستوى مجموعة المواقع المشتركة وعلى مستوى الموقع، ويتم تمكينه لكليهما بشكل افتراضي. للتمكين على مستوى مجموعة المواقع المشتركة، ضمن Site Settings>Site Collection Administration>Site Collection Collection Navigation، حدد المربع تمكين التخزين المؤقت.

تمكين التخزين المؤقت على مستوى مجموعة المواقع المشتركة.

للتمكين على مستوى الموقع، ضمنالتنقلفي إعدادات> الموقع، حدد المربع تمكين التخزين المؤقت.

تمكين التخزين المؤقت على مستوى الموقع.

استخدام التنقل المدار وبيانات التعريف في SharePoint

التنقل المدار هو خيار آخر خارج الصندوق يمكنك استخدامه لإعادة إنشاء معظم الوظائف نفسها مثل التنقل الهيكلي. يمكن تكوين بيانات التعريف المدارة لتمكين اقتطاع الأمان أو تعطيله. عند تكوينه مع تعطيل اقتطاع الأمان، يكون التنقل المدار فعالا إلى حد ما لأنه يقوم بتحميل جميع ارتباطات التنقل مع عدد ثابت من استدعاءات الخادم. ومع ذلك، فإن تمكين اقتطاع الأمان يلغي بعض مزايا الأداء للتنقل المدار.

إذا كنت بحاجة إلى تمكين اقتطاع الأمان، نوصيك بما يلي:

  • تحديث جميع ارتباطات URL المألوفة إلى ارتباطات بسيطة
  • إضافة عقد اقتطاع الأمان المطلوبة كعناوين URL مألوفة
  • تقييد عدد عناصر التنقل إلى ما لا يزيد عن 100 ولا يزيد عمقها عن ثلاثة مستويات

لا تتطلب العديد من المواقع اقتطاع الأمان، حيث غالبا ما تكون بنية التنقل متسقة لجميع مستخدمي الموقع. إذا تم تعطيل اقتطاع الأمان وإضافة ارتباط إلى التنقل لا يمكن لجميع المستخدمين الوصول إليه، فسيظل الارتباط يظهر ولكنه سيؤدي إلى رسالة مرفوضة للوصول. لا يوجد خطر من الوصول غير المقصود إلى المحتوى.

كيفية تنفيذ التنقل المدار والنتائج

هناك العديد من المقالات حول Microsoft Learn حول تفاصيل التنقل المدار. على سبيل المثال، راجع نظرة عامة على التنقل المدار في SharePoint Server.

لتنفيذ التنقل المدار، يمكنك إعداد شروط مع عناوين URL المقابلة لبنية التنقل للموقع. يمكن أيضا تنظيم التنقل المدار يدويا لاستبدال التنقل الهيكلي في كثير من الحالات. على سبيل المثال:

بنية موقع SharePoint.)

استخدام البرمجة النصية من جانب العميل المستندة إلى البحث

تتضمن إحدى الفئات الشائعة لتطبيقات التنقل المخصصة أنماط التصميم التي يعرضها العميل والتي تخزن ذاكرة تخزين مؤقت محلية لعقد التنقل.

يتمتع موفرو التنقل هؤلاء بميزتين رئيسيتين:

  • وهي تعمل بشكل عام بشكل جيد مع تصميمات الصفحات المتجاوبة.
  • إنها قابلة للتطوير للغاية وأداء لأنها يمكن أن تعرض بدون تكلفة مورد (والتحديث في الخلفية بعد انتهاء المهلة).
  • يمكن لموفري التنقل هؤلاء استرداد بيانات التنقل باستخدام استراتيجيات مختلفة، بدءا من التكوينات الثابتة البسيطة إلى موفري البيانات الديناميكيين المتنوعين.

مثال على موفر البيانات هو استخدام التنقل المستند إلى البحث، والذي يسمح بالمرونة لتعداد عقد التنقل ومعالجة اقتطاع الأمان بكفاءة.

هناك خيارات شائعة أخرى لإنشاء موفري التنقل المخصصين. راجع حلول التنقل لمداخل SharePoint للحصول على مزيد من الإرشادات حول إنشاء موفر تنقل مخصص.

باستخدام البحث، يمكنك الاستفادة من الفهارس المضمنة في الخلفية باستخدام تتبع الارتباطات المستمر. يتم سحب نتائج البحث من فهرس البحث ويتم اقتطاع النتائج من الأمان. يكون هذا بشكل عام أسرع من موفري التنقل الجاهزين عند الحاجة إلى اقتطاع الأمان. سيؤدي استخدام البحث عن التنقل الهيكلي، خاصة إذا كان لديك بنية موقع معقدة، إلى تسريع وقت تحميل الصفحة بشكل كبير. الميزة الرئيسية لهذا على التنقل المدار هي أنك تستفيد من اقتطاع الأمان.

يتضمن هذا الأسلوب إنشاء صفحة رئيسية مخصصة واستبدال رمز التنقل الجاهز ب HTML مخصص. اتبع هذا الإجراء الموضح في المثال التالي لاستبدال رمز التنقل في الملف seattle.html. في هذا المثال، ستفتح seattle.html الملف وتستبدل العنصر id="DeltaTopNavigation" بأكمله برمز HTML مخصص.

مثال: استبدال التعليمات البرمجية للتنقل الجاهز في صفحة رئيسية

  1. انتقل إلى صفحة إعدادات الموقع.
  2. افتح معرض الصفحات الرئيسية بالنقر فوق الصفحات الرئيسية.
  3. من هنا يمكنك التنقل عبر المكتبة وتنزيل الملف seattle.master.
  4. قم بتحرير التعليمات البرمجية باستخدام محرر نص وحذف كتلة التعليمات البرمجية في لقطة الشاشة التالية.
    حذف كتلة التعليمات البرمجية المعروضة.
  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 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. استبدل عنوان URL في علامة ارتساء صورة التحميل في البداية، بارتباط إلى صورة تحميل في مجموعة المواقع المشتركة. بعد إجراء التغييرات، أعد تسمية الملف ثم قم بتحميله إلى معرض الصفحات الرئيسية. يؤدي هذا إلى إنشاء ملف .master جديد.
7. HTML هذا هو العلامات الأساسية التي سيتم ملؤها بواسطة نتائج البحث التي تم إرجاعها من التعليمات البرمجية JavaScript. ستحتاج إلى تحرير التعليمات البرمجية لتغيير قيمة var root = "عنوان URL لمجموعة المواقع المشتركة" كما هو موضح في القصاصة البرمجية التالية:
var root = "https://spperformance.sharepoint.com/sites/NavigationBySearch";

8. يتم تعيين النتائج إلى الصفيف self.nodes ويتم إنشاء تسلسل هرمي من العناصر باستخدام linq.js تعيين الإخراج إلى التسلسل الهرمي الذاتي للصفيف. هذا الصفيف هو الكائن المرتبط ب HTML. يتم ذلك في الدالة toggleView() عن طريق تمرير الكائن الذاتي إلى الدالة ko.applyBinding().
يؤدي هذا بعد ذلك إلى ربط صفيف التسلسل الهرمي ب HTML التالي:
<div data-bind="foreach: hierarchy" class="noindex ms-core-listMenu-horizontalBox">

تتم إضافة معالجات الأحداث ل mouseenter و mouseexit إلى التنقل من المستوى الأعلى للتعامل مع القوائم المنسدلة في الموقع الفرعي التي تتم في الدالة addEventsToElements() .

في مثال التنقل المعقد لدينا، يظهر تحميل صفحة جديد بدون التخزين المؤقت المحلي الوقت المستغرق على الخادم تم قطعه من التنقل الهيكلي القياسي للحصول على نتيجة مماثلة لنهج التنقل المدار.

حول ملف JavaScript...

ملاحظة

إذا كنت تستخدم JavaScript مخصصا، فتأكد من تمكين CDN العام والملف في موقع CDN.

ملف 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 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");

لتلخيص التعليمات البرمجية الموضحة أعلاه في الدالة jQuery $(document).ready هناك تم viewModel object إنشاؤه ثم يتم استدعاء الدالة loadNavigationNodes() على هذا الكائن. تقوم هذه الدالة إما بتحميل التسلسل الهرمي للتنقل الذي تم إنشاؤه مسبقا والمخزن في التخزين المحلي HTML5 لمستعرض العميل أو تستدعي الدالة queryRemoteInterface().

QueryRemoteInterface() ينشئ طلبا باستخدام الدالة getRequest() مع معلمة الاستعلام المحددة مسبقا في البرنامج النصي ثم إرجاع البيانات من الخادم. هذه البيانات هي في الأساس صفيف من جميع المواقع في مجموعة المواقع المشتركة الممثلة كعناصر نقل بيانات ذات خصائص مختلفة.

ثم يتم تحليل هذه البيانات في الكائنات المعرفة SPO.Models.NavigationNode مسبقا والتي تستخدم Knockout.js لإنشاء خصائص يمكن ملاحظتها لاستخدامها بواسطة ربط البيانات بالقيم في HTML الذي قمنا بتعريفه مسبقا.

ثم يتم وضع العناصر في صفيف نتائج. يتم تحليل هذا الصفيف في JSON باستخدام Knockout وتخزينه في تخزين المستعرض المحلي لتحسين الأداء على أحمال الصفحات المستقبلية.

فوائد هذا النهج

تتمثل إحدى الفوائد الرئيسية لهذا الأسلوب في أنه باستخدام تخزين HTML5 المحلي، يتم تخزين التنقل محليا للمستخدم في المرة التالية التي يقوم فيها بتحميل الصفحة. نحصل على تحسينات رئيسية في الأداء من استخدام واجهة برمجة تطبيقات البحث للتنقل الهيكلي؛ ومع ذلك، يتطلب تنفيذ هذه الوظيفة وتخصيصها بعض الإمكانات التقنية.

في مثال التنفيذ، يتم ترتيب المواقع بنفس طريقة التنقل الهيكلي الجاهز؛ ترتيب أبجدي. إذا كنت ترغب في الانحراف عن هذا الترتيب، فسيكون تطويره وصيانته أكثر تعقيدا. يتطلب هذا الأسلوب أيضا الانحراف عن الصفحات الرئيسية المدعومة. إذا لم يتم الاحتفاظ بالصفحة الرئيسية المخصصة، فسيفوت موقعك التحديثات والتحسينات التي تقوم بها Microsoft على الصفحات الرئيسية.

التعليمات البرمجية أعلاه لها التبعيات التالية:

لا يحتوي الإصدار الحالي من LinqJS على أسلوب ByHierarchy المستخدم في التعليمات البرمجية أعلاه وسيكسر التعليمة البرمجية للتنقل. لإصلاح ذلك، أضف الأسلوب التالي إلى ملف Linq.js قبل السطر 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);
         })
     });
 },

نظرة عامة على التنقل المدار في SharePoint Server

التخزين المؤقت للتنقل الهيكلي والأداء