Подтвердить что ты не робот

Правильный способ генерации html динамически с помощью jQuery

Я нашел несколько разных и противоречивых ответов на эту тему.

Я создаю приложение, которое работает в основном с html, динамически генерируемым jQuery, на основе результатов, полученных из базового API в виде данных JSON.

Мне сказали некоторые из моих коллег (лично), что лучший способ - сделать что-то вроде этого:

var ul = $("<ul>").addClass("some-ul");
$.each(results, function(index) {
  ul.append($("<li>").html(this).attr("id", index));
});
$("body").append($("<div>").attr("id", "div-id").addClass("some-div").append(ul));

и т.д.. Причина, по которой мне говорили, что "обновляет DOM напрямую, а не анализирует html для достижения этого".

Однако, я вижу много кода, подобного этому (тот же пример):

var toAppend = '<div class="some-div" id="div-id"><ul>';
$.each(results, function(index) {
  toAppend += '<li id="' + index + '">' + this + '</li>';
});
toAppend += '</ul></div>'

Что я лично считаю не таким элегантным, но лучше ли? Я просмотрел этот вопрос за пару минут и нашел эту статью. В основном, речь идет об увеличении производительности, используя конкатенацию строк - мой "второй способ".

Основная проблема этой статьи в том, что она была выпущена в 2009 году и обсуждена версия jQuery 1.3. Сегодня текущая версия - версия 1.6.4, которая может вести себя совершенно по-другому. И это вопрос большинства статей по теме, которые я уже нашел, и я также как-то подозрительно отношусь к их достоверности.

Вот почему я решил задать здесь вопрос и спросить - какой метод генерации DOM на самом деле является правильным, исходя из производительности?

ВАЖНАЯ ИНФОРМАЦИЯ:

Я написал небольшой ориентир, чтобы проверить, какой подход лучше рассматривает производительность.

jsFiddle - версия конкатенации

jsFiddle - версия объединения массивов

код:

var text = "lorem ipsum";
var strings = $("#strings");
var objects = $("#objects");
var results = $("#results");

// string concatenation
var start = new Date().getTime();
var toAppend = ['<div class="div-class" id="div-id1"><ul class="ul-class" id="ul-id1">'];
for (var i = 1; i <= 20000; i++) {
    toAppend[i] = '<li class="li-class" id="li-id1-' + i + '">' + text + '</li>';
}
toAppend[i++] = '</ul></div>';
results.append(toAppend.join(""));
strings.html(new Date().getTime() - start);

// jquery objects
var start = new Date().getTime();
var ul = $("<ul>").attr("id", "ul-id2").addClass("ul-class");
for (var i = 0; i < 20000; i++) {
    ul.append($("<li>").attr("id", "li-id2-" + i).addClass("li-class"));
}
results.append($("<div>").attr("id", "div-id2").addClass("div-class").append(ul));
objects.html(new Date().getTime() - start);

Кажется, что работа с строками выполняется быстрее (в Firefox 7 примерно 7 раз), чем с использованием объектов и методов jQuery. Но я могу ошибаться, особенно если в этом "контрольном" коде есть ошибки или снижение производительности. Не стесняйтесь вносить какие-либо изменения.

Примечание. Я использовал Array join из-за статьи, упомянутой ранее, вместо фактической конкатенации.

EDIT: на основе предложения от @hradac я использовал фактическую конкатенацию строк в эталонном тесте, и он действительно улучшил время.

4b9b3361

Ответ 1

Прежде всего, этот микро-бенчмаркинг почти никогда не говорит вам, что вы действительно хотите знать. Во-вторых, ваши тесты различны и не эквивалентны. Например, ваш первый пример генерирует строки, которые выглядят так:

<li class="li-class" id="li-id1-493">lorem ipsum</li>

и ваши следующие строки:

<li id="li-id2-0" class="li-class"></li>

Обратите внимание на другой порядок элементов и отсутствие текста "lorem ipsum". Также нет попытки очистить результаты div между тестами, чтобы избежать проблем с производительностью в результате того, что первые 20K-результаты уже существуют.

Но помимо этих проблем возникает вопрос: "Является ли производительность на этом действительно нарушением пользовательского интерфейса на стороне клиента?" Шутки в сторону? Вы таким образом представляете такое количество текста, что видите заметные различия между альтернативными методами рендеринга текста?

Я вернусь к тому, что говорили другие, используйте механизм шаблонов. Самые быстрые из них довольно быстрые и даже имеют параметры предварительной компиляции, позволяющие повторно отображать один и тот же шаблон и получать быстрые результаты. Так что не верьте мне. Вместо этого верьте в демонстрацию. Вот мой jsFiddle, чтобы продемонстрировать производительность новой библиотеки JsRender, которая должна заменить механизм шаблона jQuery...

http://jsfiddle.net/LZLWM/10/

Примечание. Для загрузки JsRender в скрипт может потребоваться несколько секунд. Это потому, что я вытаскиваю его прямо из GitHub, и это не то, что особенно нравится GitHub. Я не рекомендую это на практике. Тем не менее, это не изменяет тайминги, и это необходимо до тех пор, пока jsFiddle не начнет включать шаблоны в качестве параметров.

Обратите внимание, что второй пример, намного ближе к реальному примеру, генерирует 20 000 строк, используя JSON в качестве отправной точки во времени примерно так же, как ваш самый быстрый тест (разница 50 мс на моей машине). Также обратите внимание, что и код, и шаблон намного понятнее и легче работать, чем когда-либо будет какой-либо беспорядок добавлений и конкатенации строк. Сколько итераций мне понадобится, чтобы получить мой шаблон в соответствии с тем, что вы делаете?

Используйте что-то простое и прекратите тратить время на этот уровень микро-оптимизации, когда он, вероятно, даже не нужен. Вместо этого используйте такие шаблоны (или любой из нескольких других хороших шаблонных движков), и убедитесь, что у вас есть устаревшие заголовки, вы используете CDN, у вас есть сжатие gzip, включенное на вашем сервере и т.д. Все материал, который YSlow говорит вам сделать, потому что это полностью поглотит последствия того, что вы смотрите здесь.

Ответ 3

В целом, я считаю, что производительность чтения превосходит производительность, если у вас нет проблем. Я бы выбрал второй подход, поскольку он легко узнаваем как стандартный фрагмент HTML, и я думаю, что он менее подвержен ошибкам.

С помощью первого метода я должен разбирать много кода в моем minhd, чтобы представить, как будет выглядеть финальная строка (и, следовательно, там, где она может потребоваться модификации).

Ответ 4

В принципе, первый метод использует несколько вызовов методов для .innerHTML для создания результата, в то время как второй метод использует только один. Это основная область, которая вызывает разницу в количестве времени, которое требуется для выполнения.

Я бы предложил третий метод, используя массив, если строки становятся чрезвычайно большими.

var toAppend = ['<div class="some-div" id="div-id"><ul>'];
$.each(results, function(index) {
  toAppend.push('<li id="' + index + '">' + this + '</li>');
});
toAppend.push('</ul></div>');
$(target).append(toAppend.join(""));

Я обычно использую метод массива только для того, чтобы быть последовательным.

Изменить: hradac прав, теперь метод concatenate выполняется быстрее.

Ответ 5

Ваш первый подход лучше. Идея состоит в том, чтобы не касаться DOM, пока вам не понадобится. В обоих примерах вы создаете UL в памяти, а затем в конце вы прикрепляете его к DOM с помощью body.append().

Однако предпочтительный способ построения дерева находится в вашем первом примере. Конкатенация строк немного медленнее (разумеется, мы говорим миллисекунды). Однако, если вам приходится делать это много раз на странице, это может стать значительным.

Я немного очистил ваш код, но только для удобства чтения:

var div = $("<div>").attr("id", "div-id").addClass("some-div");
var ul = $("<ul>").addClass("some-ul");
$.each(results, function(index) {
    var li = $("<li>").html(this).attr("id", index);
    ul.append(li);
});
div.append(ul);

// you haven't touched the DOM yet, everything thus far has been in memory

$("body").append(div); // this is the only time you touch the DOM