Angular Ivy Renderer готовность 93%
Команда Angular ведет разработку нового движка Rendered3 (Ivy), который должен ускорить работу Angular приложений и уменьшить их размер. На данный момент Ivy готов более чем на 90%, и хотя разработка еще не завершена, многие фичи уже работают.
В этой статье рассмотрим, насколько Ivy готов к реальным приложениям с внедрением зависимостей и несколькими модулями, а так же разберем внутреннее устройство некоторых элементов движка.
Обозначения
- ⚠️ — важная информация
- ✏️ — интересная информация
- 🔧 — Ivy изнутри
Быстрый старт 🚗
Создать Ivy приложение можно с помощью angular-cli(лучше обновиться до последней версии), выполнив команду ng new my-app --experemental-ivy
. Посмотрим, что поменялось, и если что-то пошло не так, сможем настроить приложение самостоятельно.
В файле tsconfig.app.json добавилась строка enableIvy, но не со значением true, как это написано в документации, а со значением “ngtsc”. Связанно это с изменением pipeline компилятора(issue).
Небольшая поправка. Все-таки нужно оставлять
true
(issue).
Запуск приложения выполняется через метод ɵrenderComponent:
BrowserModule тоже больше не нужен:
Теперь можно запускать ng serve
и смотреть на первую ошибку 😅
Issue уже есть на github, и сборка чинится запуском в aot режиме. Первое приложение заработало!!!
⚠️ После установки node modules необходимо запустить команду ivy-ngcc . Это нужно для компиляции модулей Angular в режим, совместимый с Ivy, в противном случае многие фичи и приложение могут не работать.
✏️ Наверно, все, кто смотрел исходники angular, видели символ ɵ в качестве префикса для приватных полей. Вот откуда это пошло(issue). За информацию спасибо Angular Fanatic.
🔧 После появления первого приложения можно посмотреть, во что превращается компонент после компиляции. Для этого добавим скирпт “compile”: “ngc -p src/tsconfig.app.json”
в pakcage.json. После его выполнения появится директория out-tsc, в которой будут находится js файлы. Вот как стал выглядеть AppComponent:
Компоненту добавилось статическое свойство ngComponentDef с результатами выполнения функции ɵdefineComponent. В него передаются тип компонента, селектор, фабрика для создания экземпляра и метод template для работы с шаблоном. Разберем его более подробно.
Функция AppComponent_Template состоит из набора инструкций, указывающих, какие именно ноды ДОМ дерева нужно создать и какие манипуляции с ними производить, и принимает на вход два аргумента:
- rf — сокращение от RenderFlag, который разделяет функцию на две секции. Первая(rf & 1) содержит набор инструкций, которые выполняются при создании компонента. Вторая(rf & 2) запускается при изменениях и содержит инструкции по обновлению компонента;
- ctx — контекст, т.е. данные, которые передаются из экземпляра класса компонента.
Как видно из кода выше, в инструкции, создающие ноды( ɵelementStart, ɵtext), передаются индексы. При обновлении компонента (rf & 2) происходит не полная перерисовка шаблона, а обновляются только те узлы, в которых изменился контекст. В данном случае это текстовая нода с идентификатором 1, в которой с помощью функции ɵtextBinding обновляется значение.
✏️В новом движке рендеринг шаблона производится с помощью набора специальных инструкций Ivy Instruction Set.
🔧C помощью функции ɵsetClassMetadata(строка 30) задаются метаданные компонента, ранее описанные в декораторе.
Модули
Модуль создается и добавляется в родительский так же, как раньше, и судя по инициализации приложения через компонент, есть вероятность, что команда Angular откажется от них. Поэтому данный вопрос подробно рассматриваться не будет (если интересно, можно посмотреть самостоятельно в репозитории с демо).
Data Binding
В начальном приложении в шаблон уже передавалось свойство title, после компиляции доступное как ctx.title. Теперь можно создать отдельный компонент счетчика и проверить, как обрабатываются события.
Секция шаблона после компиляции
В дереве инструкций компонента появился обработчик события клика мыши с очень длинным, но содержательным названием CounterComponent_Template_button_click_listener.
Несмотря на то, что ошибок в консоли нет, при нажатии кнопки значение счетчика не будет увеличиваться 😕. Дело в том, что на данный момент автоматическое обнаружение изменений не работает, метод template не вызывается, и код в секции rf & 2 никогда не будет выполнен (issue). Исправить это можно с помощью метода ɵmarkDirty, который сообщает планировщику, что при следующем обходе дерева компонентов(tick) для данного компонента необходимо будет вызвать метод template.
🔧В dev режиме можно отметить компонент вручную через инструменты разработчика. Для этого в консоли браузера нужно выполнить следующие команды:
✏️Теперь в stacktrace ошибки будут короче и в них будут попадать названия новых функций и методов такие как AppComponent_Template и CounterComponent_Template_button_click_listener. Это сделает отладку и отлов багов более простым, а сами stacktrace более читаемыми.
Внедрение зависимостей
Для проверки внедрения зависимостей инициализация счетчика будет вынесена в отдельный сервис и добавлена в компонент через DI.
Тут стоит обратить внимание на следующие вещи:
- ɵmarkDirty переехал в подписку, что логично, т.к. теперь изменение значения counter вызывается в ней;
- ⚠️при попытке сделать счетчик в корневом компоненте обнаружилось, что в нем не срабатывает хук ngOnInit;
- ⚠️AsyncPipe тоже запустить не удалось.
🔧В скомпилированном виде сервис будет выглядеть так:
Как и в компоненте, у сервиса есть статическое свойство, только называется оно ngInjectableDef и содержит результат выполнения функции ɵdefineInjectable (строка 14).
Помимо метода фабрики есть свойство token (строка 15), которое служит идентификатором при внедрении зависимостей.
🔧В метаданных компонента третий элемент стал массивом с описанием зависимостей (строки 15–19). В данном случае это сервис (type: CounterService).
Директивы
В Angular директивы делятся на два вида:
- атрибутивные — добавляют элементу атрибуты (class, style и т.д.), пишутся обычно в квадратных скобках;
- структурные — меняют структуру дом дерева, добавляя или удаляя ноды (*ngIf, *ngFor, *ngSwitch). Начинаются такие директивы со звездочки.
Простой компонент для проверки работоспособности директив:
⚠️ Стандартные директивы хранятся в @angular/common. Не забудьте после установки node modules выполнить ivy-ngcc, иначе при импорте модулей будут ошибки и директивы не заработают (issue).
🔧После компиляции:
Выглядит страшновато 😰, но если разобраться, все не так плохо.
На что стоит обратить внимание:
- в ngComponentRef появилось свойство directives со списком используемых структурных директив, импортированных из модуля Common (строка 56);
- в шаблоне появилась инструкция ɵtemplate (строка 50), которая и есть реализация хорошо знакомого Angular-разработчикам ng-template;
- содержимое структурных директив вынесено в отдельные функции для ngIf (строка 23) и для ngFor (строка 8), которые выполняются согласно их инструкциям и определенным в них условиям. В противном случае место ng-template занимает нода с типом COMMENT_NODE(проще говоря в DOM дереве на этом месте будет комментарий);
- для атрибутивной директивы class.ivy функция для рендера не создается, а вместо этого вызывается несколько инструкций, начиная с ɵelementClassProp(строка 17), которая, в зависимости от выполнения условия, присваивает элементу класс.
Выводы
Основным минусом является отсутствие документации. Пришлось долго танцевать с бубном, чтобы приложение запустилось. Были варианты с использованием Bazel и Rollup вместо стандартной сборки через angular-cli, и казалось, что даже Hello World не взлетит.
Но по факту оказалось, что Ivy практически готов. Можно пробовать писать тестовые приложения с его использованием и ждать официального релиза.
На данный момент нет смысла сравнивать производительность или проверять насколько лучше стал tree shaking. Но уже точно можно сказать, что возможность избавления от zone и создания приложения вообще без модулей будет удобна для маленьких проектов и создания кастомных элементов с использованием Angular Elements.
Надеюсь статья будет полезной и поможет другим разработчикам сэкономить время, избежать моих ошибок и узнать что-нибудь новое и интересное.
Ссылки
The Theory of Angular Ivy | Alex Rickabaugh | AngularConnect 2018