Оптимизация JavaScript и jQuery из-под HTML и CSS при разработке сайта

Доброго времени суток, Хабражители. Хочу поделиться неким опытом (советами) при работе с JavaScript + jQuery (по сути, вместо jQuery можете подставить любой другой JS фреймворк). Статья будет интересна новичкам JS и jQuery, но и динозаврам опытным проходить мимо не стоит, в ней вполне можно найти полезную информацию. В основном, в статье я привожу не однозначные случаи, но и место для «стоТыщРазПовтор» я счёл уместным.

image

Инициализация


Сплошь и рядом встречаю загрузку JS файлов в теге . В большинстве случаев - это не корректно! Почему? В этом случае JS начинает загружаться до загрузки HTML, и как следствие клиент дольше ждёт загрузки информации за которой он пришёл. Размещение скриптов в оправдано только в тех случаях, когда JS используется в качестве контроллера (к примеру, всё содержимое на странице мы достаём поблочно через AJAX запросы, в зависимости от URL или Hash). Если не используем, то гораздо лучше вставлять скрипты перед зыкрытием тэга . JS начнёт загружаться только после того, как посетитель увидит страницу.

Размещение инициализации скриптов непосредственно перед закрытием тега
практически не влияет на «начало» работы скриптов. В большинстве случаев используют jQuery( document ).ready( function () { ... }, что в свою очередь всё равно запускает скрипты только после загрузки HTML. Это как закупаться бензином до того, как купили автомобиль.

Inline JavaScript в HTML коде


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


Селекторы


Тема стара как мир, первым результатом в поисковике на запрос об оптимизации выдаст статью про селекторы. И тем не менее, из раза в раз, на это просто забивают. Как говориться, повторение — мать учения.

Селекторы сильно сказываются на производительности сраницы. К примеру из моей недавной практики, всего из-за одного селектора коллеги в тонне скриптов, сайт буквально висел 8-10 секунд. Для выбора инпута формы, использовался jQuery( 'input[name="some-specific-name"]' ) и это на странице с ~ 10 формами и ~ 300 полями… Замена этого селектора на ID — и, вуаля, висяк response time упал до 60-90 мс.

Буду краток: всегда используйте ID в селекторе. Если нет такой возможности, используйте ID родительского элемента, а только затем другой (класс, тег или атрибут).

jQuery( '#element-id' ); // Это супер
jQuery( '#parent-element-id .needed-class-name' ); // Это хорошо
jQuery( '#parent-element-id a' ); // Можно конечно и так


Используйте .filter(), чтобы отфильтровать элементы, вместо перебора .each()
Для выбора первых, последних или определённых элементов используйте .first(), .last(), .eq( index )

Как это относится к HTML или CSS?
Всё предельно просто, ведь селекторы из JavaScript или jQuery напрямую зависят от разметки страницы. Как быть в случае, когда в вёрстке нет ни единого класса и идентификатора (мистика, конечно, но как понятный пример должен сойти).


Анимация


Думаю уместным здесь будет упомянуть про очереди анимаций и одновременной анимации нескольких элементов — так как, это приоритарная оптимизация, но копипастить вдаваться в подробности не стану, ибо поисковик выдаст всю нужную информацию по этим пунктам (в футере приведу ссылочки).

Маргин (margin — CSS свойство) — вот первый враг анимаций.

Как-то приходилось мне разрабатывать проект с дикой анимацией и своеобразным UI (Блочный сайт, где каждый блок может быть увеличенным или приближенным. Т.е. приближение заменяет переход по ссылке, а уменьшенный вид, это что-то вроде полноэкранной навигации. Плюс размытие элементов в уменьшенном виде, плюс тач ивенты на переходы, навигация на клавиатуре, слайды, карусели, плавное появление всплывающих окон и т.д.). В кратце, ну просто всё движется одновременно.

Плавное передвижение огромных и при этом отзумленых уменьшенных изображений — это уже не всегда просто. А если таких изображений 13, и все нужно анимировать одновременно (эффект размытия)? Это полноценный вывих мозга. Тут, собственно, и был найден самый злостный враг — margin. Поскольку элементы распологались лесенкой (с разными отступами), то было необходимо использовать либо margin, либо padding, либо left. Методом тыка и множественных переборов, в моём случае отказ от margin поднял FPS с ~20 до ~35 (Opera, Firefox, IE 7,8. Chrome и так показывал 100).

Как однозначное правило выносить это не стану, только как совет, на что можно обратить внимание.

Спасибо за внимание и потраченное время, на этом всё.

Подвал

Узнать больше о jQuery Очередях [ENG]
Узнать больше о jQuery анимации нескольких элементов [ENG]
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 60

    +1
    Всё правильно. Когда то наткнулся на похожую статью, если интересна милисекундная оптимизация, то и она будет полезна.
      +1
      Спасибо за статью, не знал про margin. На счет селекторов стоит изучить эту страничку.
      На собственном опыте убедился, что $(document.getElementById('foo')) гораздо лучше чем $('#foo')
      Так же просто спасительным для меня решением в проекте с CSS анимацией стала такая оптимизация:
      -webkit-transform: translate3d(100px, 100px, 0px);
      -webkit-transition: -webkit-transform 0.5s ease;
      

      в таком случае включается аппаратное ускорение, это в разы лучше чем анимировать любыми способами атрибуты top и left. К тому же дивы можно еще и плавно уменьшать или поворачивать.
        0
        Да, CSS анимация сама по себе работает шустрее, из-за своей нативности, так сказать. Но это лучше разбирать в теме про анимацию в целом, там очень много аспектов есть на рассмотрение
          0
          На собственном опыте убедился, что $(document.getElementById('foo')) гораздо лучше чем $('#foo')

          А вот это странно, ибо на сколько я помню, конструкция $('#foo') и так выполнит document.getElementById('foo')
          github.com/jquery/jquery/blob/master/src/core/init.js (67)
            0
            Выполнить то выполнит, но совершит много лишних действий, что может ударить по производительности.
              0
              На сколько я понял, «лишними действиями» будет только разбор селектора.
              Но ради получения точных данных можно запустить пару тестов и проверить, действительно ли это так сказывается на производительности.
                0
                Это уже микро-оптимизация. На практике ни разу не видел в ней необходимости. Тем не менее, да, document.getElementById('ID') — всё равно самый быстрый.
                0
                var time = new Date().getTime();
                var div;
                for(i = 0; i < 1000000; i++){
                	//div = $(document.getElementById('select_me'));
                	div = $('#select_me');
                	div = null;
                }
                var new_time = new Date().getTime();
                alert(new_time - time);
                //div = $(document.getElementById('select_me')) => 659
                //div = $('#select_me') => 924
                


                Возможно проверка выполнена некорректно, но о результатах судить уже можно
                  +2
                  Наглядно. Но если сделать так:

                  924 - 659 = 265
                  265 / 1000000 = 0.000265

                  0.000265 сэкономленных миллисекунд за 1 селект.

                  Но в любом случае, спасибо за метод, не знал :)
                    0
                    NaN
                      0
                      Проходя мимо, замечу, что от «new Date()» (с последующим «.getTime()» и с вычитанием) в решении таких задач пора отказываться в пользу несколько более простых методов «console.time» и «console.timeEnd».

                      Они поддерживаются в Сафари, в Опере, в Файерфоксе (начиная от версии 10), в Chrome (начиная от версии 2).
                        0
                        Спасибо, буду иметь ввиду :)
                        • НЛО прилетело и опубликовало эту надпись здесь
                        • НЛО прилетело и опубликовало эту надпись здесь
                      0
                      CSS анимация очень хорошо, но вот по сегодняшний день есть баг в webkit для translate3d свойства. Дочерние элементы трансформируемого элемента с position:fixed теряют свои координаты в момент трансформации родителя.
                      Очень неприятная штука.
                      0
                      необходимо использовать либо margin, либо padding, либо left
                      Быстрее всего это делать трансформациями (CSS transform translate). В старых MSIE не работает, конечно.
                        0
                        Недавно наткнулись на одну особенность jQuery, он добавляет временный id при выборках. Это сделано для исправления багов при работе document.querySelectorAll. В нормальных браузерах, ничего плохо не происходит, но в Opera < 15 и Safari это приводит к нежелательным reflow, поэтому мы запилили свой jquery.fn.find. А так т.к. мы не используем каскад при выборке, он ещё и быстрей.
                          0
                          Все хорошо, но позволю себе вставить свое слово.

                          Вставка JS кода в head да, «блокирует» дальнейшую обработку файлов до полной загрузки скрипта. Это верное утверждение.
                          Но современные браузеры стали умнее (и коварнее). Они точно знают, что можно грузить данные в несколько потоков.
                          Это означает, что JS файлы будут грузиться синхронно с страницей\другими файлами. Поэтому такая критичность в размещении «вставок» является попросту лишней. А размещение js в одном месте — удобство и эстетика.

                          Чтобы предотвратить споры как лучше — ссылочка на веб-стандарты

                          Таким образом, Да, скрипты лучше размещать сверху. Но огромной производительности в отрисовке не прибудет
                            0
                            Ссылочка вам противоречит:
                            Итак: для того, чтобы ваша страница могла начать отрисовку как можно быстрее, размещайте стили наверху. Чтобы избежать блокировки загружаемых ресурсов, размещайте скрипты внизу.
                            Заключение о расположении стилей и скриптов :)

                            Тем более, что Энди Дэвис в первую очередь ссылался на Chrome, который пока что не монополист…
                              –2
                              В ссылке основное — сообщение Энди Дэвиса.

                              Он ссылается на Chrome, под капотом у которого Blink. А, насколько мне известно, только IE и Mozilla используют другой движок? Не уверен, но думаю, что аналогично параллельную загрузку поддерживают последние версии этих браузеров.
                              • НЛО прилетело и опубликовало эту надпись здесь
                                  0
                                  Не думаю, что Opera в версии 12,5 не практикует многопоточную загрузку.
                                  Во всяком случае, было бы интересно увидеть статью на хабре, в которой провели сравнение способов загрузки «тяжелых» страниц с помощью разных браузеров.
                                    0
                                    РТФМ. Пользователь не увидит страницу, пока не прогрузится скрипт из шапки. Скрипты всегда так делают если не объявлены дефер. Какой бы умный небыл браузер если он начнет грузить подругому, может сломать страницу.
                                  0
                                  IE, Mozilla, Safari, почти все смартфоны и планшеты — как бы аудитория вовсе не маргинальная.
                                    0
                                    IE (как минимум 10), Mozilla — не могут не пользоваться параллельной загрузкой (так как заметно бы уступали Chrome)
                                    Safari работает на WebKit, не так?
                                    Смартфоны и планшеты — здесь доминируют все те же браузеры. И опять же те же движки — WebKit, Blink. Насколько я понял и мобильная опера работает теперь на Blink — www.youtube.com/watch?v=Y70JypQCy08
                                      0
                                      Параллельная загрузка — не повод для некорректных обобщений, я больше об этом. Blink != WebKit.

                                      По сабжу: обычно скрипты имеет смысл включать перед закрывающим body и не использовать никаких onload. Если на странице несколько блоков с непересекающимся функционалом, скрипты каждого блока включаются непосредственно после блока, опять же, не отслеживая загрузку всей страницы. Это рекомендация для веб-программистов в Google и некоторых других фирмах.

                                      В качестве занятного исключения, иногда имеет смысл вынести в начало документа общие библиотеки.
                                  +1
                                  Опять же, смотрите. Про Chrome — использование приоритетов загрузки.
                                  Параллельная загрузка используется всеми браузерами. (опять же из письма Энди Дэвиса)
                                    0
                                    Особенно IE7 и IE8, которые до сих пор востребованы. Лично мне не хочется терять от 3% пользователей. А в случаях, когда заказчик к примеру банк, так и потерей всего проекта.
                                      0
                                      Если я буду неправ, поправьте пожалуйста.

                                      Реализация задачи, как правило, зависит от конкретной задачи.
                                      Нельзя найти «всегда верный» рецепт. Если целевая аудитория использует устаревшие браузеры, стоит заботится о них.
                                      Такой сервис, например как soundCloud целевой аудиторией считает пользователей IE9+.

                                      А говоря о скорости отрисовки страниц, то долгая отрисовка скорее всего означает «плохую» верстку, работу скриптов, и в меньшей степени порядок загрузки. Это влияет, да. Но менее критично (на порядок). Или я не прав?
                                      • НЛО прилетело и опубликовало эту надпись здесь
                                          +1
                                          Особенно IE7 и IE8, которые до сих пор востребованы

                                          Я даже на дату посмотрел. Думал, случайно попал в топик 2009-ого года.
                                            +1
                                            Я тоже на дату вашего комментария посмотрел. Думал, случайно увидел коммент 2020 года. Потом глянул в гугл-аналитикс, и успокоился — как был IE8 на первом месте с 26%, так и остался.

                                            А если серьезно (только вы не подумайте, что шутил про первое место и проценты, все так и есть на самом деле) — то все очень сильно зависит от аудитории сайта/приложения.
                                              0
                                              Сейчас запускаем проект на Америку, хотели отказаться от IE8, но это ~10% (против ~4% у «нас») :[
                                                0
                                                Да, я тоже говорил про штаты. Плюс, довольно консервативная в IT плане аудитория, школы не часто апгрейдят свои компьютеры.
                                      –1
                                      Размещаю скрипты снизу, перед </body>. Это позволяет обойтись без $(document).ready(function() { /* ... */ });, который преврашает асинхронный JS в исполняемый последовательно.
                                        0
                                        который преврашает асинхронный JS в исполняемый последовательно

                                        Что? Это как?
                                          –1
                                          Вызовы $(document).ready() встают в очередь и исполняются последовательно. Каждый последующий вызов не будет выполнен, если предыдущий не завершился (return). Если предыдущий вызов завершился, а, например, бросил исключение, последующие $(document).ready() вообще не сработают.

                                          Кроме того, код, который не обращается к DOM, не следует оборачивать в $(document).ready(), т. к. он будет заторможен безо всякой нужды.

                                          Пруф: github.com/jquery/jquery/blob/1.4.2/src/core.js#L243

                                          Например, вместо:

                                          $(document).ready(function(){
                                            $.ajax({
                                              url: 'ajax.php',
                                              success: function(data) {
                                                $('#foo').text(data);
                                              }
                                            });
                                          });
                                          

                                          следует делать:

                                          $.ajax({
                                            url: 'ajax.php',
                                            success: function(data) {       
                                              $(document).ready(function(){
                                                $('#foo').text(data);  
                                              });
                                            }
                                          });
                                          
                                      +2
                                      Если уж разговор зашел об оптимизации кода, в том числе и с целью увеличения производительности, тот вот эта ссылка просто бесценна:

                                      http://shichuan.github.io/javascript-patterns/

                                      Многочисленные паттерны (и антипаттерны для сравнения) написания оптимальных конструкций с обоснованием.

                                      Один из примеров:

                                      // antipattern
                                      // create and append your element
                                      $(document.body).append("<div class='baaron' />");
                                      // requery to bind stuff
                                      $("div.baaron").click(function () {...});
                                      
                                      
                                      // preferred
                                      // swap to appendTo to hold your element
                                      $("<div class='baaron' />")
                                          .appendTo(document.body)
                                          .click(function () {...});
                                      
                                        –1
                                        Не вижу в какой части статьи пошла речь об оптимизации кода
                                        +1
                                        В оптимизации кода на jWhatever все должно сводится к минимизации использования библиотеки.
                                          0
                                          Для выбора первых, последних или определённых элементов используйте .first(), .last(), .eq( index )

                                          Недавно выяснил, что $('.class:first-child') работает заметно быстрей чем $('.class').first()
                                            +1
                                            Когда создатель сайта помещает скрипты перед «</body>», то показывает элементы сайта быстрее, но зато дольше показывает их непроскриптованными.

                                            Вместо того, чтобы недоумевать «чего это сайт долго грузится», читатель принуждён недоумевать «чего это кнопка не нажимается», «чего это список не разворачивается при наведении и даже тычке», «чего это в галерее картинки не крутятся», «чего это вместо AJAX срабатывает переход на другую страницу», или как-нибудь ещё в этом же роде.

                                            Так что здесь получается не только решение проблемы (хотя и впрямь существующей), но и создание одной проблемы вместо этой. И выбор между ними надо делать трезво, в полном осознании того, какую проблему удобнее и уместнее считать для себя (для своего сайта) более существенною, чем другую.
                                              0
                                              Когда вы размещаете скрипты в <head>, вы вынуждены использовать $(document).ready(function() { /* ... */ });. Это, конечно, гарантирует, что пользователь не увидит неинтерактивного HTML, но появляется другая проблема: асинхронный JS превращается в исполняемый последовательно.
                                                +1
                                                Вот поэтому нужно оптимизировать код, минифицировать CSS/JS/изображения, использовать спрайты, lazy load, сокращать количество HTTP запросов, применять nojs фолбеки, но никак не блокировать интерфейс.
                                                  0
                                                  Есди вы назваете блокировкой интерфейса — время, после загрузки DOM, до полной инициализации ui, то это вы зря. Человеку необходимо минимум 0.4 секунды на реакцию, а так же в добавок время до какого либо действия. Скрипты выполняются быстрее, гораздо быстрее.
                                                    0
                                                    На самом деле ответ на действие пользователя превыщающий 100мс уже заметен и в большинстве случаев это раздражает.
                                                  0
                                                  Если страница поддерживает прогрессивные улучшения (сиречь работает с выключенным JS) — это и не проблема даже, а просто вариант поведения, который увидят пользователи без JS.
                                                  В общем случае Гугл рекомендует своим разработчикам не перед закрывающим body загружать скрипты, а сразу после элемента, которому эти скрипты нужны.
                                                  +1
                                                  > Inline JavaScript в HTML коде
                                                  > Во-первых, это некрасиво, неуклюже и со старта похоже на костыль.
                                                  Это не совсем так.
                                                  Если мы уж заговорили об оптимизации, то часто выгоднее иметь весь js код инлайном внутри страницы нежели ссылку, пусть даже на единственный агреггированный js файл. Пример — главная страница google где почти 80% кода страницы именно inline js.

                                                  >И наконец, эти скрипты не могут быть автоматически минифицированы и сжаты (gzip), что скажется на производительности продакшн сервера.
                                                  Этот тезис не очень понял. Отдаваемый html код, со всеми inline скриптами точно так же может быть сжат.

                                                    0
                                                    часто выгоднее иметь весь js код инлайном внутри страницы

                                                    Это чем-то обоснованно?

                                                    главная страница google где почти 80% кода страницы именно inline js

                                                    Ну, если вы заговорили про гугл… Гугл генерирует оутпут, но никак не хардкодит JS прям в шаблоне :) (это только мои догадки кончено, но код вида «google.promos.toast.cl=function(){try{window.gbar.up.sl(g,e,a.k,void 0,1)}catch(b){google.ml(b,!1,{cause:e+»_CL"})}" я бы не назвал редактируемым).
                                                      0
                                                      > Это чем-то обоснованно?
                                                      Безусловно. Время на установку соединения, и скачивания внешнего js файла даже самого маленького, на порядки больше чем время которое нужно потратить на скачивания html страницы с inline js

                                                        0
                                                        Сколько людей — столько мнений.

                                                        Я так предпочитаю упустить эту микро оптимизацию, вместо которой сделать код понятным, читабельным и редактируемым.
                                                          0
                                                          Эта микрооптимизация может стоить вам от 40мс и выше на открытие страницы.
                                                            0
                                                            Причём как в положительную, так и отрицательную сторону. От большого размера HTML некоторые браузеры имеют свойство загибаться.
                                                    0
                                                    >И наконец, эти скрипты не могут быть автоматически минифицированы и сжаты (gzip), что скажется на производительности продакшн сервера.

                                                    Вы неправы по поводу gzip. Обычно страница гзипуется вся, включая инлайн скрипты.
                                                    Вы отчасти неправы по поводу минифкации, потому что в шаблонах можно инклюдить уже собранный минифицированный файл.
                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                        0
                                                        Статья будет интересна новичкам JS и jQuery

                                                        Но решать вам конечно, для кого написана статья.

                                                        В код скрипты эмбеддят

                                                        Если идти от размера проекта:
                                                        — крохотный — ваш загрузчик бесполезная штука. Легче подключить вручную 0-2 скрипта
                                                        — средний — ваш загрузчик бесполезная штука (чуть менее бесполезней чем в прошлом пункте). Если на странице до 5 скриптов, зачем догружать ещё один?
                                                        — всё что больше среднего — для таких проектов в основном используют бекенд фреймворки. Которые уже давно научились работать с CSS и с JS (ассеты). Тут ваш загрузчик вообще бесполезен.

                                                        В частных случаях может быть иначе. Но опираться на них не стоит.
                                                        • НЛО прилетело и опубликовало эту надпись здесь
                                                            0
                                                            Тут скорее «на что» рассчитан модернайзер. По-моему так он рассчитан на своеобразные проекты, где JS преобладает над интерфейсом, без бекенд основы (либо с минимальным её использованием).
                                                            К примеру:
                                                            сайты-визитки, с разными плюшками
                                                            внешние плагины, api (ну к примеру те же гугл карты или видео плееры)
                                                            смартфонные приложения ещё можно включить в список

                                                            Если всё таки «на кого», то я так предполагаю, что на верстальщиков и скрипто-кодеров.
                                                            • НЛО прилетело и опубликовало эту надпись здесь
                                                                0
                                                                :oops: Sorry, my fault.

                                                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                      Самое читаемое