- PVSM.RU - https://www.pvsm.ru -
Приветствую вас, уважаемые читатели! Сегодня я бы хотел осветить вопрос рендеринга в веб-разработке. Конечно, на эту тему уже написано много статей, но, как мне показалась, вся информация довольно разрознена и отрывочна. По крайней мере, чтобы собрать всю картину в своей голове и осмыслить её, мне пришлось проанализировать немало информации (в основном — англоязычной). Именно поэтому я решил формализовать свои знания в статью, и поделиться результатом с сообществом Хабра. Думаю, информация будет полезна как начинающим веб-разработчикам, так и более опытным, чтобы освежить и структурировать свои знания.
Данное направление можно и нужно оптимизировать на этапе вёрстки/frontend-разработки, поскольку, очевидно, что разметка, стили и скрипты принимают в рендеринге непосредственное участие. Для этого соответствующие специалисты должны знать некоторые тонкости.
Отмечу, что статья нацелена не на точную передачу механики работы браузеров, а, скорее, на понимание её общих принципов. Тем более, разные браузерные движки сильно отличаются в алгоритмах работы, поэтому охватить все нюансы в рамках одной статьи не представляется возможным.
Для начала, рассмотрим последовательность работы браузера при отображении документа:
<head>, или элементы со стилем display:none;). Также, каждая строка текста представлена в дереве рендеринга как отдельный renderer. Каждый объект рендеринга содержит соответствующий ему объект DOM (или блок текста), и рассчитанный для этого объекта стиль. Проще говоря, render tree описывает визуальное представление DOM.В процессе взаимодействия пользователя со страницей, а также выполнения скриптов, она меняется, что требует повторного выполнения некоторых из вышеперечисленных операций.
В случае изменения стилей элемента, не влияющих на его размеры и положение на странице (например, background-color, border-color, visibility), браузер просто отрисовывает его заново, с учётом нового стиля — происходит repaint (или restyle).
Если же изменения затрагивают содержимое, структуру документа, положение элементов — происходит reflow (или relayout). Причинами таких изменений обычно являются:
class»;:hover).Браузеры по возможности локализуют repaint и reflow в пределах элементов, подвергнувшимися изменению. Например, изменение размеров абсолютно или фиксировано спозиционированного элемента затронет только сам элемент и его потомков, в то время как изменение статично спозиционированного — повлечет reflow всех элементов, следующих за ним.
Ещё одна особенность — во время выполнения JavaScript браузеры кэшируют вносимые изменения, и применяют их в один проход по завершению работы блока кода. Например, в ходе выполнения данного кода произойдет только один reflow и repaint:
var $body = $('body');
$body.css('padding', '1px'); // reflow, repaint
$body.css('color', 'red'); // repaint
$body.css('margin', '2px'); // reflow, repaint
// На самом деле произойдет только 1 reflow и repaint
Однако, как описано выше, обращение к свойствам элементов вызовет принудительный reflow. То есть, если мы в приведённый блок кода добавим обращение к свойству элемента, это вызовет лишний reflow:
var $body = $('body');
$body.css('padding', '1px');
$body.css('padding'); // обращение к свойству, принудительный reflow
$body.css('color', 'red');
$body.css('margin', '2px');
В итоге мы получим 2 reflow вместо одного. Поэтому, обращения к свойствам элементов по возможности нужно группировать в одном месте, дабы оптимизировать производительность (см. более подробный пример на JSBin [1]).
Но, на практике встречаются ситуации, когда без принудительного reflow не обойтись. Допустим, у нас есть задача: к элементу нужно применить одно и то же свойство (возьмём «margin-left») сначала без анимации (установить в 100px), а затем — анимировать посредством transition в значение 50px. Можете сразу посмотреть этот пример на JSBin [2], но я распишу его и тут.
Для начала заведём класс с transition:
.has-transition {
-webkit-transition: margin-left 1s ease-out;
-moz-transition: margin-left 1s ease-out;
-o-transition: margin-left 1s ease-out;
transition: margin-left 1s ease-out;
}
Затем, попробуем реализовать задуманное следующим образом:
var $targetElem = $('#targetElemId'); // наш элемент, по умолчанию у него присутствует класс "has-transition"
// убираем класс с transition
$targetElem.removeClass('has-transition');
// меняем свойство, ожидая, что transition отключён, ведь мы убрали класс
$targetElem.css('margin-left', 100);
// ставим класс с transition на место
$targetElem.addClass('has-transition');
// меняем свойство
$targetElem.css('margin-left', 50);
Данное решение не будет работать, как ожидается, т.к. изменения кэшируются и применяются только в конце блока кода. Нас выручит принудительный reflow, в результате код приобретёт следующий вид, и будет в точности выполнять поставленную задачу:
// убираем класс с transition
$(this).removeClass('has-transition');
// меняем свойство
$(this).css('margin-left', 100);
// принудительно вызываем reflow, изменения в классе и свойстве будут применены сразу
$(this)[0].offsetHeight; // как пример, можно использовать любое обращение к свойствам
// ставим класс с transition на место
$(this).addClass('has-transition');
// меняем свойство
$(this).css('margin-left', 50);
На основе данной статьи, а также других статей на Харбе, где освещается вопрос оптимизации JS, можно вывести следующие советы, которые пригодятся при создании эффективного фронтенда:
<head>, а скрипты — в конце <body>.div * {...} // плохо
ul li {...} // плохо
.list-item {...} // хорошо
#list-1 .list-item {...} // идеально
class», и как можно глубже в дереве DOM, это и более грамотно с точки зрения разработки и поддержки (отделение логики от представления), и менее затратно для браузера.no-hover»). Статья на эту тему [4].Для более детального изучения вопроса рекомендую ознакомиться со статьями:
Надеюсь, каждый читатель извлёк из статьи что-нибудь полезное. В любом случае — спасибо за внимание!
Автор: skutin
Источник [7]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/news/60736
Ссылки в тексте:
[1] более подробный пример на JSBin: http://jsbin.com/duhah/2/edit
[2] этот пример на JSBin: http://jsbin.com/qutev/1/edit
[3] рекомендаций по составлению селекторов: http://learn.jquery.com/performance/optimize-selectors/
[4] Статья на эту тему: http://habrahabr.ru/post/204238/
[5] How browsers work: http://taligarsiel.com/Projects/howbrowserswork1.htm
[6] Rendering: repaint, reflow/relayout, restyle: http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/
[7] Источник: http://habrahabr.ru/post/224187/
Нажмите здесь для печати.