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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Деякі перерахунки дерева відтворення можуть бути більш «важкими». Варто розглядати ці операції в термінах дерева відтворення: якщо зміни стосуються тільки вузла, який є прямим нащадком тіла документа (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-вузла у браузера. Цей запит змушує браузер виконати всі відкладені операції, щоб видати актуальну інформацію. Все це призводить до виконання перерахунку (і мабуть, перемальовування).

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

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

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

  • Не змінюйте індивідуальні стильові властивості одне за іншим. Для масштабованості коду і збільшення швидкості реакції інтерфейсу потрібно міняти імена класів, а не самі стилі. Але це передбачає статичні класи. Якщо ж стилі повністю динамічні (наприклад, «спливання» елементів), то необхідно редагувати властивість cssText замість того, щоб вносити зміни в властивість style самого елемента.
  • Збирайте зміни DOM-дерева «офлайн» і застосовуйте їх скопом. «Офлайн» в даному випадку означає, що зміни відбуваються не на живому DOM-дереві. наприклад:
    • використовуйте documentFragment для проміжних результатів,
    • клонуйте ваші вузли, змінюйте копії, а потім замінюйте ними оригінали,
    • ховайте елемент за допомогою display: none (1 перерахунок + перерисовка), потім застосовуйте всі зміни, потім відновлюйте display (ще один перерасчте + перерисовка). Тки чином можна заощадити сотні потенційних перерахунків дерева відтворення.
  • Чи не запитуйте фактичні стилі надто часто. Якщо ви працюєте з обчисленими значеннями, то запитайте його одного разу, закешіруйте в локальну змінну і працюйте з локальною копією. Стосовно до негативному прикладу, наведеного вище:
  • Щоразу перед проведенням змін розраховуйте, який ефект вони нададуть на дерево відтворення (можна прикидати за кількістю 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).

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Тепер давайте протестуємо ту ж саму сторінку в 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 вводиться поняття «перерахунку стилів» (стилі, що не змінюють геометрію) проти «макета».

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

Читати далі