Новости

Візуалізація: отрисовка, перерахунок дерева / макета, стилізація

  1. процес відтворення
  2. За деревами не видно лісу
  3. Вихідний HTML-код:
  4. Перемальовування і перерахунок дерева відтворення
  5. Що викликає перерахунок або перемальовування
  6. браузери розумнішими
  7. Зменшення перерахунків і перерісовок
  8. Інструменти
  9. практичні приклади
  10. Висновок
  11. Читати далі

Примітка: переклад замітки Rendering: repaint, reflow / relayout, restyle від Stoyan Stefanov. Висвітлюється дуже багато прикладних аспектів відтворення сторінок в браузерах і побудови дерева потоку документа. Примітки далі курсивом. Термін reflow далі перекладається як «перерахунок дерева отрисовка», щоб відокремити його від терміна repaint (фактична перерисовка сторінки).

Отже, що відбувається в браузері, коли він отрісовиваєт HTML-сторінку на екрані? Як він обробляє видану йому кашу з HTML-, CSS- і (можливо) JavaScript-шматків?

процес відтворення

Різні браузери проробляють це по-різному, але наступна діаграма дає розуміння про рух в цілому і більш-менш підходить для всіх браузерів, як тільки вони завантажили обраний код сторінки.

  • Браузер аналізує HTML-код сторінки (кашу з тегів) і будує деякий DOM-дерево - представлення даних, в якому кожному HTML-тегу відповідає свій вузол, а текстові шматки між тегами відповідають своїм текстовим вузлів. Кореневим вузлом такого дерева є documentElement (тег <html>). Витрати на час побудови DOM-дерева досліджувалися в цих статтях .
  • Браузер аналізує CSS-код, намагається зрозуміти все закладеним в ньому хакі і коректно розпізнати всі відомі йому прийоми, серед яких можуть бути -moz, -webkit і інші розширення, які він може не розуміти і повинен ігнорувати. Інформація по стилям будується каскадним чином: спочатку йдуть правила за замовчуванням з самого браузера (більш докладно їх розмір досліджувався в цій статті ), Потім йдуть користувача стилі, потім авторські (автора сторінки) - зовнішні, імпортовані, внутрішні, а вже потім - всі стилі, прописані в атрибутах style HTML-тегів.
  • Після цього настає найцікавіша частина: браузер будує дерево відтворення. Дерево відтворення в якійсь мірі пов'язано з DOM-деревом, але не відповідає йому повністю. Дерево відтворення знає про поточні стилях (тому якщо ви заховаєте div за допомогою display: none, то він в це дерево не потрапить, це не зовсім вірно, як видно з наступної статті , Але принцип той). Те ж саме для інших невидимих ​​елементів: наприклад, для тега head і всіх вкладених елементів. З іншого боку в цьому дереві DOM-вузли можуть бути представлені у вигляді декількох вузлів, наприклад, якщо кожен рядок <p> вимагає свого вузла для відтворення. Вузол в такому дереві називається кадр (frame) або блок (box) (так само як і CSS-блок, відповідний блокової моделі ). У кожного з цих блоків є блокові CSS-властивості: ширина, висота, межа, поля, і т.д (таким чином, навіть малі (inline) елементи будуть представлені в дереві відтворення окремими блоками).
  • Після того як готове дерево відтворення, його можна нарешті і отрисовать (paint, draw) на екрані.

За деревами не видно лісу

Давайте розглянемо наступний приклад.

Вихідний HTML-код:

<Html> <head> <title> Beautiful page </ title> </ head> <body> <p> Колись давно-давно тут був довгий-довгий параграф ... </ p> <div style = "display : none "> Приховане повідомлення </ div> <div> <img src =" ... "/> </ div> ... </ body> </ html>

DOM-дерево, яке відповідає даному HTML-документу, в основному, містить по одному вузлу на кожен тег і по одному вузлу на кожен шматок тексту між тегами (для простоти можна ігнорувати той факт, що прогалини теж є текстовими вузлами):

documentElement (html) head title body p [текстовий вузол] div [текстовий вузол] div img ...

Це дерево відтворення є видимою частиною DOM-дерева. У ньому відсутня ряд речей - наприклад, заголовок сторінки і приховані елементи - але є і додаткові вузли (або кадри, або блоки) для відображення рядків тексту.

корінь (RenderView) body p рядок 1 рядок 2 рядок 3 ... div img ...

Кореневий елемент дерева відтворення є кадром (блоком), що містить всі інші елементи. Можна вважати його внутрішньою частиною вікна браузера, так як він обмежує область, в якій сторінка може розташовуватися. Говорячи технічно мовою, в WebKit кореневих вузлом називається RenderView, і він відповідає початкового контейнеру по специфікації CSS, і є прямокутною областю видимості, що розповсюджується від початку сторінки (0, 0) до (window.innerWidth, window.innerHeight).

Давайте далі подивимося, як саме відображення елементів на екрані викликає рекурсивний перегляд дерева перемальовування. Тут і далі термін (re) flow перевдена як перерахунок дерева перемальовування.

Перемальовування і перерахунок дерева відтворення

На сторінці завжди присутній хоча б один розрахунок потоку документа разом з його отрисовкой (якщо звичайно ви не фанат порожньої сторінки в браузері :)). Після цього зміна вхідної інформації, яка була використана для рендеринга сторінки, може привести до одного (або обох) пунктам нижче.

  1. Буде потрібно перерахувати деякі частини дерева перемальовування (або навіть всі дерево). Цей процес називається перерахунок дерева відтворення (reflow) або перерахунок макета документа (re-layout). Зауважте, що завжди є хоча 1 такий перерахунок: при первинному відображенні сторінки.
  2. Деякі частини екрану потрібно оновити, або тому що змінилися геометричні властивості вузлів, або будь-які їх стилі (наприклад, колір фону). Цей процес називається перерисовка (repaint, redraw).

Перемальовування і перерахунки можуть бути вельми витратним заходом, негативно впливати на відчуття користувача і сповільнювати загальну реакцію інтерфейсу на дії користувача.

Що викликає перерахунок або перемальовування

Все, що будь-яким чином змінює інформацію, використовувану для побудови дерева відтворення (що, природно, призводить до необхідності пересозданія такого дерева), може призвести до перерахунку або перемальовуванні. наприклад:

  • Додавання, видалення або оновлення DOM-вузлів.
  • Приховування DOM-вузла за допомогою display: none (перерахунок або перерисовка) або visibility: hidden (тільки перерисовка, геометрія не змінюється).
  • Рух, анімація DOM-елемента на сторінці.
  • Додавання файлу стилів, зміна стильових властивостей елементів.
  • Призначені для користувача дії, наприклад, зміни розміру вікна, зміна розміру шрифту або (тільки не це) прокрутка сторінки.

Давайте розглянемо кілька прикладів:

var bstyle = document.body.style; // кешіруем bstyle.padding = "20px"; // перерахунок + перерисовка bstyle.border = "10px solid red"; // ще один перерахунок + перерисовка bstyle.color = "blue"; // тільки перерисовка, розміри не змінилися bstyle.backgroundColor = "#fad"; // перерисовка bstyle.fontSize = "2em"; // перерахунок + перерисовка // новий DOM-елемент = перерахунок + перерисовка document.body.appendChild (document.createTextNode ( 'dude!'));

Деякі перерахунки дерева відтворення можуть бути більш «важкими». Варто розглядати ці операції в термінах дерева відтворення: якщо зміни стосуються тільки вузла, який є прямим нащадком тіла документа (body), то цілком ймовірно, що інші вузли не будуть порушені. Але що станеться, якщо ви почнете анімувати і збільшити блок у верхній частині сторінки, що буде зміщувати всю решту сторінку вниз? Мабуть, це буде досить затратно.

браузери розумнішими

Оскільки перасчети потоку документи і перемальовування, пов'язані зі зміною дерева відтворення, є досить непростими операціями, браузери намагаються всіма силами нівелювати негативні ефекти. Один підхід полягає у відмові виконувати частину роботи. По крайней мере, не в даний момент. Браузер може створити деяку чергу, в яку буде вносити зміни, вироблені скриптами, і виконувати ці зміни пачками. У цьому випадку кілька змін, кожне з яких спричинить перерахунок, можуть бути об'єднані в одне, і буде проведений тільки один перерахунок. Браузери також можуть скидати стан черги (малювати) сторінку по закінченню деякого часу (100-200мс) або досягнення певної кількості (10-20) таких змін.

Але іноді скрипти можуть запобігти такому оптимизирующее поведінку браузерів і змусити застосувати всі накопичені в черзі зміни прямо цю секунду. Це відбувається, коли ви запитуєте наступну інформацію про стилях:

  1. offsetTop, offsetLeft, offsetWidth, offsetHeight,
  2. scrollTop / Left / Width / Height,
  3. clientTop / Left / Width / Height,
  4. getComputedStyle () або currentStyle в IE.

Всі вищенаведені варіанти подають запит на інформацію про стилі DOM-вузла у браузера. Цей запит змушує браузер виконати всі відкладені операції, щоб видати актуальну інформацію. Все це призводить до виконання перерахунку (і мабуть, перемальовування).

Наприклад, поганою ідеєю буде встановлювати стилі, запитуючи при цьому інформацію про них в одному і тому ж місці (наприклад, всередині циклу):

// ні, тільки не це! el.style.left = el.offsetLeft + 10 + "px";

Зменшення перерахунків і перерісовок

Для зменшення негативних ефектів від перерахунку / перемальовування (щодо призначеного для користувача сприйняття) варто, перш за все, звести до мінімуму будь-які звернення до стильової інформації, щоб браузер міг максимально оптимізувати перерахунок дерева відтворення. Що для цього потрібно?

  • Не змінюйте індивідуальні стильові властивості одне за іншим. Для масштабованості коду і збільшення швидкості реакції інтерфейсу потрібно міняти імена класів, а не самі стилі. Але це передбачає статичні класи. Якщо ж стилі повністю динамічні (наприклад, «спливання» елементів), то необхідно редагувати властивість cssText замість того, щоб вносити зміни в властивість style самого елемента. // погано var left = 10, top = 10; el.style.left = left + "px"; el.style.top = top + "px"; // краще el.className + = "theclassname"; // або якщо top і left обчислюються на льоту, то краще ... el.style.cssText + = "; left:" + left + "px; top:" + top + "px;";
  • Збирайте зміни DOM-дерева «офлайн» і застосовуйте їх скопом. «Офлайн» в даному випадку означає, що зміни відбуваються не на живому DOM-дереві. наприклад:
    • використовуйте documentFragment для проміжних результатів,
    • клонуйте ваші вузли, змінюйте копії, а потім замінюйте ними оригінали,
    • ховайте елемент за допомогою display: none (1 перерахунок + перерисовка), потім застосовуйте всі зміни, потім відновлюйте display (ще один перерасчте + перерисовка). Тки чином можна заощадити сотні потенційних перерахунків дерева відтворення.
  • Чи не запитуйте фактичні стилі надто часто. Якщо ви працюєте з обчисленими значеннями, то запитайте його одного разу, закешіруйте в локальну змінну і працюйте з локальною копією. Стосовно до негативному прикладу, наведеного вище: // нет, тільки не це! for (тут; довгий; цикл) {el.style.left = el.offsetLeft + 10 + "px"; el.style.top = el.offsetTop + 10 + "px"; } // краще var left = el.offsetLeft, top = el.offsetTop esty = el.style; for (тут; довгий; цикл) {left + = 10; top + = 10; esty.left = left + "px"; esty.top = top + "px"; }
  • Щоразу перед проведенням змін розраховуйте, який ефект вони нададуть на дерево відтворення (можна прикидати за кількістю DOM-вузлів). Наприклад, використання абсолютного позиціонування перетворює елемент в дочірній для тіла всього дерев відтворення, тому він не торкнеться такої кількості вузлів, наприклад, при анімації. Деякі елементи (що знаходяться глибше по дереву відтворення) можуть бути в області видимості цього елемента, але вони зажадають тільки перемальовування, а не перерахунку дерева.

Інструменти

Приблизно рік тому не було нічого, що забезпечувало б інформацією про перерахунок та перемальовуванні сторінки в браузері (це може бути і не так: я підозрюю, що в MS були свої внутрішні засоби для розробників, але про них ніхто не знав, бо вони були поховані в глибині MSDN: P). Тепер ситуація змінилася, і дуже сильно.

По перше, з'явилося подія MozAfterPaint в нічних збірках Firefox, тому стали можливі такі плагіни, як, наприклад, цей (Від Kyle Scholz), який використовує цю подію. mozAfterPaint - це дуже прикольно, але воно повідомляє тільки момент перемальовування (а не перерахунку дерева відтворення, що є більш витратним).

Далі. DynaTrace Ajax і більш пізній SpeedTracer від Google (зауважте: обидва Трейсі (trace) :)) є відмінними інструментами для відстеження перерахунків і перерісовок, перший призначений для IE, другий - для WebKit.

Одного разу в минулому році Douglas Crockford зауважив, що ми, можливо, робимо в CSS дуже дурні речі, але самі про них не знаємо. І тепер є, що заперечити на це. Я трохи брав участь в проекті на тій стадії, що ми розбиралися з наступною проблемою: незначне збільшення користувачем шрифту (in IE6) завантажує CPU на 100% протягом 10-15 хвилин, перш ніж сторінка знову з'являлася на екрані.

Відмінно, інструменти у нас є, тому для нас вже не може бути поблажок в плані ідіотського поведінки в CSS.

Ну, за винятком, може бути, одного: було б, напевно, круто, якби існував інструмент, який би показував дерево відтворення на додаток до DOM-дереву?

практичні приклади

Давайте трохи докладніше поглянемо на зазначені інструменти і продемонструємо з їх допомогою різницю між зміною стилів (restyle, зміни в дереві відтворення не стосуються геометрії), перерахунком (reflow, який змінює макет документа) і перемальовуванням (repaint).

Давайте порівняємо два способи зробити одне і те ж. Спочатку ми будемо міняти деякі стилі (що не торкаються макет) і після кожної зміни будемо запитувати стильова властивість, ніяк зі зміною не пов'язане.

bodystyle.color = 'red'; tmp = computed.backgroundColor; bodystyle.color = 'white'; tmp = computed.backgroundImage; bodystyle.color = 'green'; tmp = computed.backgroundAttachment;

А потім те ж саме, тільки всі інформаційні запити будуть здійснюватися після всіх змін в стилях:

bodystyle.color = 'yellow'; bodystyle.color = 'pink'; bodystyle.color = 'blue'; tmp = computed.backgroundColor; tmp = computed.backgroundImage; tmp = computed.backgroundAttachment;

В обох випадках ось визначення використовуваних змінних:

var bodystyle = document.body.style; var computed; if (document.body.currentStyle) {computed = document.body.currentStyle; } Else {computed = document.defaultView.getComputedStyle (document.body, ''); }

Тепер нехай ці два приклади зміни стилів будуть виконуватися при натисканні на документ. Тестова сторінка знаходиться тут: restyle.html (Натискаємо "dude"). Давайте назвемо її тестом зміни стилів (restyle test).

У другому тесті буде майже те ж саме, тільки ми будемо також змінювати і деталі компонування:

// кожен раз запитуємо стилі bodystyle.color = 'red'; bodystyle.padding = '1px'; tmp = computed.backgroundColor; bodystyle.color = 'white'; bodystyle.padding = '2px'; tmp = computed.backgroundImage; bodystyle.color = 'green'; bodystyle.padding = '3px'; tmp = computed.backgroundAttachment; // запитуємо стилі тільки в самому кінці bodystyle.color = 'yellow'; bodystyle.padding = '4px'; bodystyle.color = 'pink'; bodystyle.padding = '5px'; bodystyle.color = 'blue'; bodystyle.padding = '6px'; tmp = computed.backgroundColor; tmp = computed.backgroundImage; tmp = computed.backgroundAttachment;

Цей випадок давайте назвемо тестом зміни макета (relayout test), исходник тут .

Ось що видає DynaTrace для першого тесту (стилі).

Ось що видає DynaTrace для першого тесту (стилі)

Спочатку у нас сторінка завантажилася, потім я кликаю один раз, щоб виконати перший сценарій (запит стилів кожен раз, приблизно в 2 с). Потім кликаю ще раз, щоб виконати другий сценарій (запити до стилям після всіх змін, приблизно в 4 с)

Цей інструмент показує, в який момент закінчилася завантаження сторінки (це означає логотип IE). Потім курсор миші знаходиться на ділянці, що відповідає за обробку кліка. Збільшуємо масштаб (круто!), І ось більш детальна картина:

Тут вже дуже добре видно, скільки часу пішло на JavaScript (синя смужка) і скільки - на отрисовку (зелена смужка). Це у нас ще простий приклад, а як відрізняються смужки по довжині? Наколько дорожче для браузера обходиться отрисовка, ніж виконання JavaScript? Зазвичай в Ajax- і Rich-додатках JavaScript не є вузьким місцем, гальмує доступ до DOM-елементів та частина, що відповідає за отрисовку сторінки.

Дуже добре, тепер запускаємо другий тест - на зміну макета, - який змінює геометрію. Цього разу перевіряємо "PurePaths". Це тимчасова шкала + більш детальна інформація про кожен елемент на цій шкалі. Я підсвітив перший клік, де JavaScript викликав відкладені операції зі зміни макета.

Я підсвітив перший клік, де JavaScript викликав відкладені операції зі зміни макета

Знову збільшуємо масштаб і бачимо, що додатково до смужці «отрисовка» у нас з'явилася ще одна - «перерахунок дерева відтворення», тому що в цьому тесті ми його свідомо створюємо на додаток до перемальовуванні.

Знову збільшуємо масштаб і бачимо, що додатково до смужці «отрисовка» у нас з'явилася ще одна - «перерахунок дерева відтворення», тому що в цьому тесті ми його свідомо створюємо на додаток до перемальовуванні

Тепер давайте протестуємо ту ж саму сторінку в Chrome і подивимося на результати SpeedTracer.

Це перший тест зі зміни стилів зі збільшеним масштабом і оглядом того, що відбувається.

Якщо коротко, то після кліка відбувається отрисовка. Але в разі першого кліка 50% часу йде на перерахунок стилів. Чому? Тому що ми запитуємо інформацію про стилях (навіть не пов'язану з самими змінними стилями) після кожної зміни.

Збільшуємо події і бачимо в деталях, що відбувається насправді: після першого кліка стилі перераховуються 3 рази. Після другого - тільки один раз.

Після другого - тільки один раз

Давайте тепер запустимо тест на зміну макета. Загальна картина виглядає так само, як і в минулий раз:

Загальна картина виглядає так само, як і в минулий раз:

Але в разі детального перегляду ми бачимо, що перший клік викликав три перерахунку дерева відтворення (оскільки запитував інформацію про обчислених стилях), а другий - тільки один.

Але в разі детального перегляду ми бачимо, що перший клік викликав три перерахунку дерева відтворення (оскільки запитував інформацію про обчислених стилях), а другий - тільки один

Незначна відмінність між цими інструментами полягає в тому, що SpeedTracer не вказує той момент, коли завдання зі зміни макета запланована і додана в чергу, а DynaTrace показує. І тому DynaTrace не відображує різницю між зміною стилів і зміною макета, як SpeedTracer. Може бути, просто сам IE робить між ними різниці? Також DynaTrace не показав три перерахунку дерева відтворення в першому випадку другого тесту (коли ми запитуємо інформацію про стилях після кожної зміни макета). Може бути, це так IE працює?

Нижче наведені додаткові дані щодо цих двох тестів після запуску їх достатню кількість разів.

  • У Chrome відмова від запиту обчислених стилів при їх зміні приблизно в 2,5 рази швидше (тест на зміну стилів) і 4,42 рази швидше, якщо ви міняєте і стилі і макет (тест на зміну макети).
  • У Firefox - в 1,87 і 1,64 рази швидше, відповідно.

За всіма браузерам зміна стилів приблизно в два рази швидше, ніж зміна стилів і макета сторінки. За винятком IE6, де різниці становить 4 рази.

Висновок

Спасибі, что дочитали до цього місця. Спробуйте погратися з інструментами, озвученими вище, і подивитися за швидкість відтворення сторінок. Давайте я ще раз нагадаю ті терміни, що використовувалися в статті.

  • Дерево відтворення - візуальна частина DOM-дерева.
  • Вузли в дереві відтворення називають кадри або блоки.
  • Зміна дерева відтворення називається перерахунком (reflow) в Mozilla і макетом (layout) у всіх інших браузерах, мабуть.
  • Оновлення на екрані результатів перерахунку дерева відтворення називається перемальовуванням (repaint, redraw) (в IE / DynaTrace).
  • У SpeedTracer вводиться поняття «перерахунку стилів» (стилі, що не змінюють геометрію) проти «макета».

І трохи посилань на прощання. Варто зазначити, перші три з наведених матеріалів пояснюють ситуацію більш глибоко з точки зору браузера, а не з точки зору розробника (як це спробував зробити я).

Читати далі

Всі Коментарі (habrahabr.ru)

Отже, що відбувається в браузері, коли він отрісовиваєт HTML-сторінку на екрані?
Як він обробляє видану йому кашу з HTML-, CSS- і (можливо) JavaScript-шматків?
Але що станеться, якщо ви почнете анімувати і збільшити блок у верхній частині сторінки, що буде зміщувати всю решту сторінку вниз?
Що для цього потрібно?
Ну, за винятком, може бути, одного: було б, напевно, круто, якби існував інструмент, який би показував дерево відтворення на додаток до DOM-дереву?
Це у нас ще простий приклад, а як відрізняються смужки по довжині?
Наколько дорожче для браузера обходиться отрисовка, ніж виконання JavaScript?
Чому?
Може бути, просто сам IE робить між ними різниці?
Може бути, це так IE працює?

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

Или позвоните нам по телефонам: (048) 823-25-64

Организация (обязательно) *

Адрес доставки

Объем

Как с вами связаться:

Имя

Телефон (обязательно) *

Мобильный телефон

Ваш E-Mail

Дополнительная информация: