Правильный способ генерации 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 я использовал фактическую конкатенацию строк в эталонном тесте, и он действительно улучшил время.
Ответы
Ответ 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 говорит вам сделать, потому что это полностью поглотит последствия того, что вы смотрите здесь.
Ответ 2
Проверьте шаблоны jquery -
http://api.jquery.com/category/plugins/templates/
Ответ 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