Медленное выполнение JS в IE и FF - PullRequest
0 голосов
/ 03 февраля 2012

У меня есть эта функция, которая работает с возвращенными данными JSON.Это действительно быстро в Chrome, но медленно в IE и FF.Предложения о том, как я могу улучшить это?Возвращенные данные - около 15 объектов.Это создает кучу якорей вверху со списками под каждым заголовком.

function list_response(jsonData) {
    "use strict";
    var lists = document.getElementById("lists"), anchors = document.getElementById("anchors"), jItems = jsonData.items;
    jItems.sort(function (a, b) {
            return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
            });
    for (var i = 0; i < jItems.length; i++) {
        var pList = jItems[i], str = "", ank = "";
        str += '<span class="backtotop">[ <a href="#">Back to top</a> ]</span><br /><br /><br /><li class="title nodot"><a class="pHeader" name="' + pList.name + '"><h2>' + pList.name + '</h2></a></li>';
        ank += '<a class="pHeader" href="#' + pList.name + '">' + pList.name + '</a>&nbsp; ';
        lists.innerHTML += str;
        anchors.innerHTML += ank;

        for (var j = 0; j < jsonData.items[i]["videos"].length; j++) {
            var vList = jsonData.items[i]["videos"][j];
            var strs = "";
            strs += '<li class="nodot"><a href="https://www.site.org/videos/Video_Player.page?bctid=' + vList.id + '">' + vList.name + '</a></li>';
            lists.innerHTML += strs;
        }
    }
}

Ответы [ 6 ]

4 голосов
/ 03 февраля 2012

Вот версия вашего кода, которая сочетает в себе следующие улучшения производительности:

  1. Добавлять в innerHTML только один раз в конце цикла.Вы хотите избегать делать это так часто, как только можете, потому что каждый раз, когда вы добавляете в него элемент, происходит повторная обработка всего HTML-кода в этом элементе.Это минимизирует количество транзакций с DOM, которое может быть самой медленной частью всей процедуры.Эта улучшенная версия значительно сокращает количество транзакций DOM.Если jItems.length было 20, а среднее количество видео было 5, это сократило бы количество транзакций DOM до 1/50 числа транзакций DOM.
  2. Скопируйте строковые данные в массив с .push() и делать .join() в самом конце, вместо того, чтобы каждый раз добавлять в конец строки.Механизм JS часто может объединять множество строк одновременно гораздо эффективнее, чем объединять каждый фрагмент по частям.
  3. При накоплении строковых данных в массивах больше нет необходимости инициализировать временные значения в каждом цикле или подцикле..
  4. Вместо того, чтобы получить pList и затем иметь четыре ссылки на pList.name, просто получите значение имени один раз и используйте его напрямую.
  5. Кэш jItems[i] в цикле, потому что на него ссылаютсяв нескольких местах, а не пересчитывать его каждый раз.
  6. Вычислить переменную len для каждого цикла for только один раз и сравнить с ней, а не пересчитывать ее на каждой итерации.
  7. Cache jItems[i]["videos"] один разво внешнем цикле, а не повторять его каждый раз во внутреннем цикле.
  8. Если в jsonData.items содержится большое количество элементов, алгоритм сортировки не очень эффективен, так как он должен пересчитывать нижний регистрверсия каждого имени для каждого сравнения.Вы можете предварительно собрать все строчные версии за один проход (один раз для каждого элемента), а затем просто использовать их в алгоритме сортировки, а не повторять их каждый раз при сравнении двух элементов.

Эти изменения приводят кэтот код:

function list_response(jsonData) {
    "use strict";
    var lists = document.getElementById("lists"), anchors = document.getElementById("anchors"), jItems = jsonData.items;
    var results = [], anks = [], vList, pListName, item, videoItem;
    // precache all lower case versions to make sorting faster
    var i, iLen = jItems.length, j, jLen;
    for (var i = 0; i < iLen; i++) {
        jItems[i].nameLower = jItems[i].name.toLowerCase();
    }
    jItems.sort(function (a, b) {
        return a.nameLower.localeCompare(b.nameLower);
    });
    for (i = 0; i < iLen; i++) {
        item = jItems[i];                   // cache for use in loops
        videoItem = item["videos"];      // cache for use in loops
        pListName = item.name;            // cache for use in multiple places
        results.push('<span class="backtotop">[ <a href="#">Back to top</a> ]</span><br /><br /><br /><li class="title nodot"><a class="pHeader" name="' + pListName + '"><h2>' + pListName + '</h2></a></li>');
        anks.push('<a class="pHeader" href="#' + pListName + '">' + pListName + '</a>&nbsp; ');

        for (j = 0, jLen = videoItem.length; j < jLen; j++) {
            vList = videoItem[j];
            results.push('<li class="nodot"><a href="https://www.site.org/videos/Video_Player.page?bctid=' + vList.id + '">' + vList.name + '</a></li>');
        }
    }
    lists.innerHTML += results.join("");
    anchors.innerHTML += anks.join("");
}
2 голосов
/ 03 февраля 2012

Вы можете попробовать кеширование длины - например, создайте переменную l = jItems.length и используйте ее в условии цикла for вместо того, чтобы каждый раз получать свойство length - в зависимости от реализации обычно возникают издержкипри поиске длины массива, поэтому, если у вас большой массив, эта разница может быть заметна.

Кроме того, создайте временные переменные, чтобы сохранить строки str и ank по мере их увеличения, а затем ставить толькоих в innerHTML прямо в конце.

1 голос
/ 03 февраля 2012

Старый трюк и не очень уверен, что это актуально, но

var builder = [];
builder.push('foo');
builder.push('bar');
builder.push('baz');
var str = builder.join('')

раньше был намного быстрее в IE, чем

var str = '';
str += 'foo';
str += 'bar';
str += 'baz';
1 голос
/ 03 февраля 2012

вы не должны использовать строки и innerHTML для создания вашего HTML. Вы должны использовать DOM.

0 голосов
/ 03 февраля 2012

Основным узким местом производительности здесь, несомненно, является способ работы с innerHTML.

el.innerHTML += aString;, конечно, эквивалентно el.innerHTML = el.innerHTML + aString;.

Это означает, что для каждого вызова DOM будет сериализован в строку HTML, а затем десериализован обратно в действительный DOM. Это много ненужной работы.

Вместо этого либо:

(a) Создайте весь HTML заранее и присвойте innerHTML только один раз для каждого элемента.

(b) Добавление элементов или фрагментов документа в родительский узел.

0 голосов
/ 03 февраля 2012
var strHTML = "";
var ankHTML = "";

for (var i=0; i < jItems.length; i++){
  ...    
  strHTML += str;
  ankHTML += ank;
  ...
}
lists.innerHTML = strHTML ;
anchors.innerHTML = ankHTML ;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...