Angular — наш основной инструмент для написания приложения TestMace. В одной из прошлых статей мы затронули тему Ivy рендерера. Самое время поподробнее узнать, чем Ivy отличается от предыдущего движка.
В нашей компании Nrwl мы уже какое-то время находимся в предвкушении возможностей, которые откроет нам и нашим клиентам Ivy. Angular Ivy — это новый движок рендеринга Angular, кардинально отличающийся от всех аналогичных технологий популярных фреймворков тем, что он использует Incremental DOM.
Что такое Incremental DOM и чем он отличается от Virtual DOM?
Проведём сравнительный анализ и выясним почему Incremental DOM является верным решением для Angular.
Принцип работы Virtual DOM
React — довольно распространённый фреймворк, в котором впервые использовался Virtual DOM. Основная идея состоит в следующем:
Каждый компонент создаёт новое VDOM-дерево всякий раз, когда он рендерится. React сравнивает новое дерево с предыдущим, после чего вносит набор изменений в DOM браузера, чтобы привести его в соответствие с новым VDOM-деревом.
Virtual DOM имеет два основных преимущества:
- Возможность использования любого языка программирования для реализации функции рендеринга компонента и отсутствие необходимости в компиляции. React-разработчики главным образом пишут на JSX, но вполне подойдёт и обычный JavaScript.
- В результате рендеринга компонента мы получаем значение, что может пригодиться при тестировании, отладке и т.п.
Incremental DOM
Incremental DOM используется компанией Google для внутренних нужд. Его основная идея такова:
Каждый компонент компилируется в набор инструкций, которые создают DOM-деревья и непосредственно обновляют их при изменении данных.
К примеру, вот этот компонент:
@Component({
selector: 'todos-cmp',
template: `
<div *ngFor="let t of todos|async">
{{t.description}}
</div>
`
})
class TodosComponent {
todos: Observable<Todo[]> = this.store.pipe(select('todos'));
constructor(private store: Store<AppState>) {}
}
Будет скомпилирован в:
var TodosComponent = /** @class */ (function () {
function TodosComponent(store) {
this.store = store;
this.todos = this.store.pipe(select('todos'));
}
TodosComponent.ngComponentDef = defineComponent({
type: TodosComponent,
selectors: [["todos-cmp"]],
factory: function TodosComponent_Factory(t) {
return new (t || TodosComponent)(directiveInject(Store));
},
consts: 2,
vars: 3,
template: function TodosComponent_Template(rf, ctx) {
if (rf & 1) { // create dom
pipe(1, "async");
template(0, TodosComponent_div_Template_0, 2, 1, null, _c0);
} if (rf & 2) { // update dom
elementProperty(0, "ngForOf", bind(pipeBind1(1, 1, ctx.todos)));
}
},
encapsulation: 2
});
return TodosComponent;
}());
Функция template содержит инструкции для рендеринга и обновления DOM. Обратите внимание, что инструкции не интерпретируются движком рендеринга фреймворка. Они и есть движок рендеринга.
Преимущества Incremental DOM
Почему же в Google решили остановить свой выбор на Incremental DOM, а не на Virtual DOM?
Задача, которую они поставили, — сделать так, чтобы приложения показывали хорошую производительность на мобильных устройствах. А значит, была необходима оптимизация размера бандла и объёма потребляемой памяти.
Чтобы решить поставленные выше задачи:
- Движок рендеринга должен быть tree-shakable
- Движок рендеринга не должен потреблять много памяти
Incremental DOM и tree shakability
При использовании incremental DOM фреймворк не интерпретирует компонент; вместо этого компонент ссылается на инструкции. Если какая-либо инструкция осталась нетронутой, то она не будет использоваться в будущем. Поскольку данная информация известна во время компиляции, можно исключить неиспользуемые инструкции из бандла.
Для работы Virtual DOM необходим интерпретатор. В момент компиляции неизвестно, какая именно его часть понадобится, а какая — нет, поэтому необходимо загнать его в браузер целиком.
Incremental DOM и потребление памяти
Virtual DOM создаёт всё дерево с нуля при каждом повторном рендеринге.
Incremental DOM же не требует памяти для повторного рендеринга представления, если оно не вносит изменения в DOM. Память необходимо будет выделить только в том случае, если будут добавлены или удалены DOM-узлы, а объём выделяемой памяти будет пропорционален производимым изменениям в DOM.
Так как большинство вызовов render/template не вносят никаких изменений (или вносимые ими изменения незначительны), существенная экономия памяти обеспечена.
Incremental DOM победил?
Разумеется, всё не так просто. Например, то, что render-функция возвращает значение, предоставляет отличные возможности, скажем, в тестировании. С другой стороны, пошаговое выполнение инструкций с помощью Firefox DevTools упрощает отладку и профилирование производительности. Эргономичность того или иного способа зависит от используемого фреймворка и предпочтений разработчика.
Ivy + Incremental DOM = ?
Angular всегда строился на использовании HTML и шаблонов (пару лет назад я опубликовал пост, в котором обозначил свои мысли в поддержку данного решения и его эффективности в долгосрочной перспективе). Именно поэтому главный козырь Virtual DOM никогда не будет выигрышным для Angular.
С учётом всего этого, tree shakability и низкого потребления памяти, я считаю разумным выбором использование Incremental DOM в качестве основы нового движка рендеринга.
Если вам необходима консультация по Angular, информация об обучении или поддержке, вы можете почитать о наших методах работы с клиентами здесь