Цикл рендерингу застосунків, створених за допомогою Angular. Взаємодія браузера, Angular і zone.js
Усім привіт, це Макс Корецький, principal engineer в kawa.ai та засновник indepth.Dev. У цій статті поговоримо про деякі особливості роботи з Angular.
Дисклеймер: ця стаття є перекладом оригіналу, опублікованого тут. Переклад з англійської мови організовано редакцією DOU.
Сучасний вебстак складається з багатьох рухомих компонентів. Розгляньмо усі компоненти типового застосунку на Angular та їхні функції.

Браузер генерує DOM, щоб описати, що має відображатися на екрані, і API — для керування цією візуалізацією. Він запускає JavaScript у відповідь на якісь асинхронні події, ініційовані діями користувача.
У JavaScript-коді зазвичай виділяють код фреймворку і код застосунку. Код застосунку впроваджує бізнес-логіку, яка обробляє вхідні дані та оновлює стан застосунку. Завдання коду фреймфорку полягає в тому, щоб перетворити стан застосунку в оновлення DOM. Загальна назва цього процесу — рендеринг, але він має різні назви в різних фреймворках. У Angular цей процес називається «виявлення змін».
Таке «виявлення змін» в Angular автоматично запускається після кожної асинхронної події. Причиною цього є припущення, що більшість подій викликають зміну стану застосунку, яка має відображатися в DOM і, відповідно, на екрані.
Щоб Angular міг знати, коли може змінитися стан застосунку, йому потрібно знати, коли відбуваються події. Ось тут у гру вступає бібліотека zone.js. Ця бібліотека патчить API платформи браузера, щоб можна було перехопити всі асинхронні події в браузері. Angular прив’язується до хуків, виявлених zone.js, і використовує сповіщення про події DOM, тайм-аути, AJAX/XHR, Promise тощо як підказку для запуску виявлення змін.
Angular безпосередньо не взаємодіє з zone.js, а натомість використовує NgZone, який є своєрідною обгорткою навколо zone.js, щоб обмежити кількість подій, про які має стати відомо Angular.
Розгляньмо кожен крок на такому простому прикладі:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<div class="container">
<div>
<button (click)="fetchData()">Fetch data</button>
<div>title: {{title}}</div>
</div>
</div>
`,
...
})
export class AppComponent {
title = null;
async fetchData() {
const response = await fetch('http://example.com/movies.json')
const todo = await response.json();
this.title = todo.title;
}
}
Ось що станеться, коли натиснути кнопку:
- браузер виявляє click і додає обробника подій до черги подій (браузера);
- zonej починається з zoneAwareCallback, який просто запускає функцію зворотного виклику, зареєстровану Angular;
- angular виконує обгортку навколо зворотного виклику з шаблону компонента; обгортка позначає вигляд і всіх його попередників «брудними»;
- angular запускає метод компонента
fetchDataчерез функцію event listener, зареєстрований у функції шаблону компонента; - бізнес-логіка всередині коду застосунку
fetchDataвиконує мережевий запит; - запит перехоплюється
zone.js, яка планує макрозавдання в браузері; - тепер подія обробляється,
zone.jsзапускає onMicrotaskEmpty черезNgZone; - Angular реагує, запускаючи виявлення змін у застосунку через ApplicationRef.tick;
- виявлення змін оновлює DOM і запускає інші пов’язані ефекти.
На цьому етапі JavaScript передає керування браузера. Подію оброблено, бізнес-логіка оновила стан програми, а Angular оновив DOM під час виявлення змін. Тепер час, щоб браузер відобразив оновлення на екрані та продовжив виконання макрозавдання, пов’язаного із запитом на нову роботу.
Браузер оновлює екран, проходячи через звичайний конвеєр:
- [Рендеринг] Обчислення стилю. Це процес визначення того, які правила CSS застосовуються до яких елементів, на основі відповідних селекторів, наприклад
.headlineабо.nav > .nav__item. Тепер, коли правила відомі, вони застосовуються, та обчислюються остаточні стилі для кожного елемента. - [Рендеринг] Макет. Коли браузер знає, які правила застосовуються до елемента, він може почати обчислювати, скільки місця той займає та де він знаходиться на екрані. Модель вебмакета означає, що один елемент може впливати на інші, наприклад, ширина елемента
<body>зазвичай впливає на ширину його дочірніх елементів і так далі вгору та вниз по дереву, тому процес може бути досить складним для браузера. - [Малювання] Малювання. Малювання — це процес заповнення пікселів. Він передбачає малювання тексту, кольорів, зображень, рамок і тіней, по суті, кожної візуальної частини елементів. Зазвичай малюнок виконується на кількох поверхнях, які часто називають шарами.
- [Малювання] Композиція. Оскільки частини сторінки були намальовані потенційно на кількох шарах, їх потрібно намалювати на екрані в правильному порядку, щоб сторінка відтворювалася правильно. Це особливо важливо для елементів, які перекривають інші, оскільки помилка може призвести до неправильного відображення одного елемента над іншим.
Більше про це можна почитати за цим посиланням.
Ця діаграма відображає взаємодію між вищезазначеними компонентами.

Ви можете чітко побачити операції, описані вище, за допомогою профайлера Chrome Dev Tools. Ось колстек для обробника події click:

І ось завдання браузера після того, як JavaScript передає керування браузеру:

Сподобалась стаття? Підписуйтесь на автора, щоб отримувати сповіщення про нові публікації на пошту.
2 коментарі
Додати коментар Підписатись на коментаріВідписатись від коментарів