Как увеличить скорость работы jQuery скрипта

    11 правил, следуя которым можно увеличить производительность скрипта, написанного с использованием jQuery.

    Вот разметка, на основе которой будут примеры:

    <div id="content">
    	<form method="post" action="/">
    		<h2>Traffic Light</h2>
    		<ul id="traffic_light">
    			<li><input type="radio" class="on" name="light" value="red" /> Red</li>
    			<li><input type="radio" class="off" name="light" value="yellow" /> Yellow</li>
    			<li><input type="radio" class="off" name="light" value="green" /> Green</li>
    		</ul>
    		<input class="button" id="traffic_button" type="submit" value="Go" />
    	</form>
    </div> 
    


    1. Искать элемент нужно по его аттрибуту id. Если у элемента нет id, то нужно указывать ближайший к нему элемент с id:

      var active_light = $('#traffic_light .on');

      Если мы ищем элемент через id, то это происходит быстро, потому что используется метод DOM getElementById.

    2. Использовать имя тега перед названием класса:

      var active_light = $('#traffic_light input.on');

      Тогда используется метод DOM getElementsByTagName и среди выбранных элементов ищется тот, у которого есть класс 'on'.
      Выборка элементов по названию класса — одна из самых медленных выборок в jQuery.

      Но никогда не надо ставить имя тега перед id:
      var active_light = $('div#traffic_light');

      Будут выбраны все элементы с тэгом 'div', а уже потом среди них будет найден элемент с нужным id.
    3. Кешируйте объекты jQuery:
      Если вы используете объект больше чем один раз, вместо того, чтобы поворять несколько раз выборку елемента:
      $('#traffic_light input.on).bind('click', function(){...});
      $('#traffic_light input.on).css('border', '3px dashed yellow');
      $('#traffic_light input.on).css('background-color', 'orange');
      $('#traffic_light input.on).fadeIn('slow');

      сохраните его в локальную переменную:
      var $active_light = $('#traffic_light input.on');
      $active_light.bind('click', function(){...});
      $active_light.css('border', '3px dashed yellow');
      $active_light.css('background-color', 'orange');
      $active_light.fadeIn('slow');

      '$' перед именем переменной можно ставить, чтобы показать что в ней храниться объект jQuery.

    4. Выстраивайте код в цепочки:
      Поскольку большинство методов jQuery возвращают после выполнения объект jQuery, то предыдущий пример можно написать так:
      var $active_light = $('#traffic_light input.on');
      $active_light.bind('click', 
       function(){return false;})
      	.css('border', '3px dashed yellow')
      	.css('background-color', 'orange')
      	.fadeIn('slow');
      


      Это позволит нам сократить размер кода.
    5. Используйте подзапросы:
      var $traffic_light = $('#traffic_light'),
      	$active_light = $traffic_light.find('input.on'),
      	$inactive_lights = $traffic_light.find('input.off');
      


      Поскольку мы сохранили объект в переменной, то следующие выборки будут производиться только в сохраненном объекте, а не во всем дереве элементов.

    6. Минимизируйте работу с DOM-деревом
      Поскольку манипуляции (удаление, перемещение и вставка новых элементов) с деревом DOM довольно медленные, то вместо того, чтобы вставлять каждый элемент в DOM:
      var top_100_list = [...], 
      $mylist = $('#mylist'); 
      
      for (var i=0, l=top_100_list.length; i<l; i++)
      {
      	$mylist.append('<li>' + top_100_list[i] + '</li>');
      }
      


      лучше сначала создать набор элементов в переменной, чтобы потом за один шаг вставить его в DOM:
      var top_100_list = [...], 
      $mylist = $('#mylist'), 
      top_100_li = ""; 
      
      for (var i=0, l=top_100_list.length; i<l; i++)
      {
      	top_100_li += '<li>' + top_100_list[i] + '</li>';
      }
      
      $mylist.html(top_100_li);
      

    7. Используйте делегирование событий:
      Каждое событие «всплывает» в DOM дереве к элементам-предкам. Это можно использовать когда нескольким элементам на одно и то же событие нужно выполнить одинаковую функцию.

      Вместо того, чтобы привязывать к каждому из этих элементов свое событие:
      $('#entryform input).bind('focus', function(){
      	$(this).addClass('selected');
      }).bind('blur', function(){
      	$(this).removeClass('selected');
      });
      


      можно привязать событие к элементу-родителю:
      $('#entryform).bind('focus', function(e){
      	var cell = $(e.target);  
      
      	cell.addClass('selected');
      }).bind('blur', function(e){
      	var cell = $(e.target);
      	cell.removeClass('selected');
      });
      

    8. Привязывать к странице только относящиеся к ней скрипты.
      Обычно все складывается в один большой скрипт, который вставляется на все страницы сайта и выполняется на событие $(document).ready(). Но даже если функция не работает на данной конкретной странице, то все равно тратится время на поиск элементов, к которым привязана эта функция.

      Поэтому можно разделить скрипт на несколько частей или же структирировать его сдедующим образом:
      var mylib =
      {
      	article_page :
      	{
      		init : function()
      		{
      			...
      		}
      	},
      	traffic_light :
      	{
      		init : function()
      		{
      			...
      		}
      	}
      }
      


      а на странице, где мы хотим использовать функции, относящиеся, например, к traffic_light перед закрывающим </body> вставить:
      <script type="text/javascript">
          mylib.article.init();
      </script>
      </body>
      

    9. Задержать загрузку до $(window).load
      Чаще всего разработчики jQuery выполнение скрипта привязывают к событию $(document).ready(). Событие происходит в то время рендеринга страницы, когда объекты все еще загружаются. Иногда можно наблюдать что страница подвисает во вермя загрузки. Причиной этого могут быть именно функции, которые привязаны к $(document).ready().

      Чтобы избавиться от этого, можно часть функций привязать к событию $(window).load(), которое происходит уже после того, как все содержимое страницы (включая содержимое iframe) уже загружено.
      $(window).load(function(){
      	// функции jQuery, инициализируемые после загрузки страницы
      });
      

    10. Сжимать js код
      Хотя это не и не имеет отношения именно к jQuery.
      Это можно делать с помощью YUICompressor, Dojo ShrinkSafe, или же Google Closure Compiler.
    11. Изучайте jQuery
      Читайте документацию, там можно найти много интересного.
      Можно скачать cheatsheet по jQuery 1.3, чтобы функционал был в нужные моменты перед глазами.


    Это довольно вольный перевод вот этой статьи, которая попалась мне на глаза. Большинство из этих пунктов я знал и использовал, но нашел для себе еще немного нового.
    Share post

    Comments 43

      0
      Спасибо статью! Большинство этих советов можно отнести не только к jQuery, но и к другим фреймворкам и pure JS.

      P.S.: опечатка: getElementByTagName → getElementsByTagName
        0
        спасибо, исправил.
        –8
        Боян
          +4
          Минусуйте, но это так.
            +4
            Согласен. Вдобавок не авторский и плохо отформатированный
          0
          Совет №3 бесполезен по сути — jQuery кеширует селекторы.
            +9
            Запросы кешируются только если браузер поддерживает DOM Mutation Events. IE не поддерживает эти события и селекторы не кешируются. Всегда можно самому краткоскрочно закешировать хотя бы банальный $(this) в each-методе.
              +3
              Не знал про это, спасибо, интересно.
              –1
              Я не нашел упоминания об этом в докуменатции. И при просмотре кода jQuery тоже не увидел того, что селекторы кэшируются. Я, правда, не очень подробно смотрел.

              Может быть направите меня на то место где в jQuery механизм кеширования?
              Интересно было бы посмотреть подробнее.
                –1
                Я когда-то читал, но сейчас нагуглить не могу почему-то. Пардон.
                  –1
                  а может оно не в самом jQuery а в Sizzle который отвечает за селекторы. только ничего похожего на кеширование в основном методе Sizzle не увидел :)
                0
                Хорошая статья, но подобное я уже видел где-то тут по ссылке…
                +1
                Спасибо, статья интересная. Было бы совсем здорово, если бы были приведены примеры того на сколько эти подходы увеличивают скорость (например в следующей статье ;) ).

                P.S. понятное дело, что на разных страницах будет разное время, но можно просто выбрать какую-нибудь страницу в интернете с большим количеством DOM объектов.
                  +1
                  Выборка элементов по названию класса — одна из самых медленных выборок в jQuery.

                  Самая медленная операция в выборке: фильтрация уникальных элементов после того, как было что-то выбрано. А утверждение про медленную выборку по классам отчасти справедливо для старых браузеров, в новых используется getElementsByClassName() и querySelectorAll(), первый работает довольно быстро

                  Задержать загрузку до $(window).load

                  Я бы не стал делать таких громких заявлений: есть большая вероятность, что событие долго не сработает, если грузится какой-нибудь счетчик с медленного сервера.
                    0
                    На самом деле не все так однозначно, как написано в статье, все зависит от браузера. Например, в современных браузерах, где есть поддержка querySelectorAll следование некоторым пунктам из статьи наоборот замедлит выполнение. Хорошие статьи по производительности jqueryна русском:
                    http://mabp.kiev.ua/2009/08/10/presentation-from-coffee-n-code/
                    http://mabp.kiev.ua/2009/03/29/jquery-profiling/
                      –1
                      спасибо, почитаю
                      ну то есть идем к тому, чтобы разделят код на куски — для старых и медленных и для новых и быстрых =)
                      +1
                      Почти все знал, но все равно полезно, полезно не только для JQuery но и для других фреймворков, общий смысл, он один.
                        0
                        habrahabr.ru/blogs/jquery/52201/
                        Уже было. Относительно новым можно считать 8 и 9 пункт.
                          –1
                            +1
                            Ну и ещё первый пункт там спорный, он ориентирован больше для зарубежных разработчиков, а не для российских реалий. Лучше загружать jQuery с Яндекса:
                            js.static.yandex.net/jquery/1.3.2/_jquery.js
                            По сравнению с Гуглом, у этого способа гораздо больше шансов оказатся в кеше российского пользователя. А даже если и не окажется в кеше, то загрузится в 3-4 раза быстрее, чем с Гугла (во-первых потому что сервер Гугла зарубежом, соответственно большой пинг; во-вторых потому что у Яндекса с многими провайдерами договорённости о локальном трафике).
                            +9
                            11 правил: 1, 2, 3, 4, 5, 6, 7, 8, 9 :)
                              +1
                              про делегирование событий отдельное спасибо. как-то не задумывался :)
                                +1
                                Про пункт 1 — насколько я помню Sizzle начинает искать справа налево, а не слева направо. Т.е. он сначала найдет все .of, а потом уже будет смотреть, находятся ли они в #traffic_light.
                                  +1
                                  Да, но если первый селектор стоит #id, то он ставится в качестве context запроса:
                                  кусок кода Sizzle

                                  Иначе было бы очень странно $('#my a') — выбрать все анкоры страницы и среди них искать те, у которых есть родитель с id='my'…
                                  0
                                  Пункт 9. Задержать загрузку до $(window).load.
                                  Как-то сомнительно.
                                  Событие window.onload будет вызвано только когда будут загружены все внешние объекты.
                                  А это может вызвать нехорошую ситуацию, когда что-либо будет грузиться слишком долго, например, счетчик, или баннер с другого сайта.
                                  И соответственно инициализация страницы может значительно затянуться.
                                    0
                                    Да. И надо на это закладываться и не делать так везде и всюду.
                                    Поэтому и не предлагаю перемещать туда инициализацию всего и вся.
                                    0
                                    А вы тестировали на сколько быстрей работает
                                    $(' .my_class') по сравнению с $('div.my_class')?
                                    Если элементов пару разницы нету, если много (в одном проекте было больше 10000 ) не поможет
                                    Еще бы цифры для сравнения.
                                      0
                                      Нужно указывать источник перевода.
                                        0
                                        Я указал, но из-за того что некорректно закрыл тег предыдущей ссылки, кусок текста съелся. Да, и источник был другой. =)
                                        0
                                        Используйте подзапросы:
                                        var $traffic_light = $('#traffic_light'),
                                        $active_light = $traffic_light.find('input.on'),
                                        $inactive_lights = $traffic_light.find('input.off');
                                        Поскольку мы сохранили объект в переменной, то следующие выборки будут производиться только в сохраненном объекте, а не во всем дереве элементов.
                                          +1
                                          читайте документацию :)

                                          не надо придумывать подзапросы, за вас всё придумали
                                          называется это context

                                          var $traffic_light = $('#traffic_light'),
                                          $active_light = ('input.on', $traffic_light),
                                          $inactive_lights = ('input.off', $traffic_light);

                                        0
                                        А не подскажете по конструкции $('#mylist').ready()? Есть ли смысл, как влияет на производительность, когда срабатывает?
                                          0
                                          смысл есть если это id фрейма
                                            0
                                            Нет, просто обычный div
                                            Хочется создать действие, происходящее сразу после загрузки элемента, но до того, как загрузится вся страница
                                              0
                                              Binds a function to be executed whenever the DOM is ready to be traversed and manipulated
                                              $(document).ready возникает когда елементы DOM готовы к манипуляциям. а не тогда когда загрузится вся страница (window.onload)
                                                +1
                                                тоесть в вашем случае, $('#mylist').ready() не имеет смысла
                                                0
                                                $(document).ready в некоторых случаях возникает даже до загрузки стилей
                                            –1
                                            Спасибо за статью, я бы еще добавил к сжимателям js кода <a href=«www.crockford.com/javascript/jsmin»jsmin
                                            • UFO just landed and posted this here
                                                –1
                                                Спасибо. Полезно, грамотно и познавательно.
                                                  0
                                                  Подправьте в примере №8:

                                                  mylib.article.init();
                                                  


                                                  на

                                                  mylib.article_page.init();
                                                  

                                                  Only users with full accounts can post comments. Log in, please.