Выполнение обнаружения изменений Angular Ivy: вы готовы?

Выполнение обнаружения изменений Angular Ivy: вы готовы?

От автора: в то время как новый визуализатор Ivy не является полностью особенным, многие люди задаются вопросом, как он будет работать и какие изменения он подготавливает для нас. В этой статье я собираюсь визуализировать механизм того, как происходит обнаружение изменений Ivy, показать некоторые вещи, которые мне действительно нравятся, а также создавать простые приложения, основанные на инструкциях, похожих на инструкции Angular Ivy, с нуля.

Во-первых, давайте представим приложение, которое я собираюсь исследовать здесь:

Я создал онлайн-демоверсию, которую я использую, чтобы понять, как она работает: //alexzuza.github.io/ivy-cd/

Демо использует компилятор angular 6.0.1 aot. Вы можете щелкнуть любой блок жизненного цикла, чтобы перейти к определению. Чтобы запустить процесс обнаружения изменений, просто введите что-то в одно из тех полей, которые ниже Sub-Child.

Представление

Конечно, представление является основной абстракцией низкого уровня в Angular. Для нашего примера мы получим что-то вроде:

В представлении должен описываться шаблон, чтобы он содержал данные, которые будут отражать структуру этого шаблона. Давайте посмотрим на представление ChildComponent. Оно имеет следующий шаблон:

В то время как текущий механизм представления создает узлы из фабрики определения представления и сохраняет их в массиве узлов определения представления

Ivy создает LNodes из инструкций, написанных в функции ngComponentDef.template, и сохраняет их в массиве данных:

Помимо узлов, новое представление также содержит привязки в массиве данных (см. data[4], data[5], data[6] на рисунке выше). Все привязки для данного представления сохраняются в том порядке, в котором они отображаются в шаблоне, начиная с bindingStartIndex

Обратите внимание, как я получаю экземпляр просмотра из ChildComponent. ComponentInstance .__ ngHostLNode__ содержит ссылку на узел узла компонента. (Другой способ — ввести ChangeDetectorRef)

Таким образом, Angular сначала создает корневой вид и находит хост-элемент в индексе 0 в массиве data

а затем проходит через все компоненты и заполняет массив data для каждого представления.

Обнаружение изменений

Хорошо известный ChangeDetectorRef — это просто абстрактный класс с абстрактными методами, такими как detectChanges, markForCheck и т. д.

Когда мы запрашиваем эту зависимость в конструкторе компонента, мы фактически получаем экземпляр ViewRef, который расширяет класс ChangeDetectorRef.

Теперь давайте рассмотрим внутренние методы, которые используются для запуска обнаружения изменений в Ivy. Некоторые из них доступны как public api (markViewDirty и detectChanges ), но я не уверен в других.

detectChanges

Синхронно выполняет обнаружение изменений на компоненте (и, возможно, его подкомпонентах).

Эта функция запускает обнаружение изменений синхронно на компоненте. Существует очень мало причин для вызова этой функции напрямую, так как предпочтительным способом обнаружения изменений является использование markDirty (см. ниже) и дождаться, когда планировщик вызовет этот метод в какой-то будущий момент. Это связано с тем, что одно действие пользователя часто приводит к тому, что многие компоненты становятся недействительными, а одновременное обнаружение изменений на каждом компоненте будет неэффективным. Лучше подождать, пока все компоненты не будут отмечены как загрязненные, а затем выполните единое обнаружение изменений по всем компонентам

tick

Используется для обнаружения изменений во всем приложении. Это эквивалентно detectChanges, но вызывается на корневой компонент. Кроме того, tick выполняет хуки жизненного цикла и условно проверяет компоненты на основе их ChangeDetectionStrategy и грязи.

scheduleTick

Используется для планирования обнаружения изменений для всего приложения. В отличие от tick, scheduleTick объединяет несколько вызовов в один проход обнаружения изменений. Обычно это называется косвенно, вызывая markDirty когда представление нужно повторно markDirty.

markViewDirty (markForCheck)

Отмечает текущий вид и все предки грязными. Если раньше в Angular 5 он только повторялся вверх и включал проверки всех родительских представлений, теперь учтите, что markForCheck запускает цикл обнаружения изменений в Ivy !!!

markDirty

Помечает компонент как грязный (требуется обнаружение изменений). Отметка о загрязнении компонента запланирует обнаружение изменений на этом компоненте в какой-то момент в будущем. Пометка уже грязного компонента как грязного — это заглушка. Только одно замечательное обнаружение изменений может быть запланировано для каждого дерева компонентов. (Два компонента, загруженные с помощью отдельного «renderComponent», будут иметь отдельные планировщики)

checkNoChanges

Ничего нового:) Когда я отлаживал новый механизм обнаружения изменений, я заметил, что забыл intall zone.js. И, как вы уже догадались, он отлично работал без этой зависимости и без cdRef.detectChanges или tick.

Почему?

Как вы, вероятно, знаете, по дизайну триггеры Angular меняют обнаружение для компонента onPush только в том случае, если (см. мой ответ на stackoverflow).

Эти правила также применяются к Ivy:

Меняется одно из полей //github.com/angular/angular/blob/43d62029f0e2da0150ba6f09fd8989ca6391a355/packages/core/src/render3/instructions.ts#L890

Связанное событие, вызванное компонентом или его дочерними элементами //github.com/angular/angular/blob/43d62029f0e2da0150ba6f09fd8989ca6391a355/packages/core/src/render3/instructions.ts#L1743

Ручной вызов markForCheck (функция markViewDirty отвечает за это сейчас (см. ниже))

У меня есть (input) выходное связывание в SubChildComponent. Второе правило приведет к вызову markForCheck. Поскольку мы уже узнали, что этот метод фактически вызывает обнаружение изменений, теперь должно быть ясно, как это работает без zonejs.

А если выражение изменилось после проверки?

Не волнуйся, он все еще здесь 🙂

Порядок изменения обнаружения

С анонса Ivy команда Angular прилагает напряженные усилия, чтобы гарантировать, что новый двигатель правильно обработает все хуки жизненного цикла в правильном порядке. Это означает, что порядок операций должен быть схожим.

Макс NgWizard K написал в своей большой статье: Как вы можете видеть, все знакомые операции все еще здесь. Но порядок операций, похоже, изменился. Например, кажется, что теперь Angular сначала проверяет дочерние компоненты и только затем встроенные представления. Поскольку на данный момент нет компилятора для вывода, подходящего для проверки моих допущений, я не знаю точно.

Вернемся к ChildComponent в моем простом приложении

С моей стороны было предназначено написать один sub-child как обычный компонент перед другими, которые находятся внутри встроенного представления. Теперь пришло время увидеть это в действии:

Мы можем выполнить первую проверку встроенного представления, а затем регулярного компонента. Таким образом, здесь нет изменений с предыдущего движка.

Во всяком случае, в моей демонстрации есть опция «запустить Angular компилятор», и мы можем протестировать другие случаи.

//alexzuza.github.io/ivy-cd/

Инициализация однократной строки

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

Это так называемая однократная инициализация строк. В документации Angular сказано:

Angular устанавливает его и забывает об этом.

Что касается меня, это означает, что Angular не будет делать никаких дополнительных проверок для этой привязки. Но то, что мы фактически видим в angular5 — это то, что он проверяется за каждый цикл обнаружения изменений во время вызова updateDirectives.

Теперь давайте посмотрим, как это должно быть в новом движке:

Поскольку мы можем видеть, что компилятор Angular хранит нашу константу за пределами кода, который отвечает за создание и обновление компонента и использует это значение только в режиме создания. Angular больше не создает текстовые узлы для контейнеров

Обновление: //github.com/angular/angular/pull/24346

Даже если вы не знаете, как работает Angular ViewContainer под капотом, вы можете заметить следующую картину при открытии devtools:

В режиме production мы видим только <!——>. И вот вывод Ivy:

Я не могу быть уверен в 100%, но, похоже, у нас будет такой результат, как только Ivy станет стабильным. В результате query в коде ниже

будет возвращать null, поскольку Angular не должен больше читать ElementRef с помощью собственного элемента, указывающего на комментарий узла DOM из контейнеров

Инкрементный DOM (IDOM) с нуля

Давным-давно Google анонсировала так называемую инкрементальную библиотеку DOM.

Библиотека фокусируется на создании DOM-деревьев и предоставлении динамических обновлений. Она не предназначался для использования напрямую, а как цель компиляции для шаблонных движков. И, похоже, у IVy есть что-то общее с инкрементной библиотекой DOM.

Давайте создадим простое приложение с нуля, что поможет нам понять, как работает рендер IDOM. демонстрация. Наше приложение будет иметь счетчик, а также распечатать имя пользователя, которое мы будем вводить, вводя элемент ввода.

Предположим, что на странице уже есть элемент input и button:

И все, что нам нужно сделать, это визуализировать динамический html, который будет выглядеть так:

Чтобы сделать это, давайте напишем elementOpen, elementClose и текстовые «инструкции» (я называю это так, потому что Angular использует такие имена, как IVy, можно рассматривать как особый вид виртуального процессора).

Сначала нам нужно написать специальные помощники для перемещения дерева узлов:

Теперь давайте напишем инструкции:

Иными словами, эти функции просто проходят через узлы DOM и вставляют узел в текущее положение. Также текстовые инструкции задают свойство data, чтобы мы могли видеть текстовое значение браузера.

Мы хотим, чтобы наши элементы были способны поддерживать некоторое состояние, поэтому давайте представим NodeData:

Теперь давайте изменим нашу функцию renderDOM чтобы мы не добавили новый элемент в DOM, если в текущей позиции уже есть то же самое:

Обратите внимание на мой комментарий /*, key */. Было бы лучше, если бы у наших элементов был некоторый ключ к различию элементов. После этого добавим логику, которая будет отвечать за обновления текстовых узлов

То же самое можно сделать для узлов элементов.

Затем давайте напишем функцию patch, которая будет принимать элемент DOM, функцию обновления и некоторые данные, которые будут потребляться функцией обновления:

Наконец, давайте протестируем наши инструкции:

Результат можно найти здесь

Вы также можете проверить, что код обновит только текстовый узел, содержимое которого изменилось, проверив его с помощью инструментов браузера:

Таким образом, основная концепция IDOM заключается в том, чтобы просто использовать реальный DOM для сравнения с новыми деревьями. Это все. Спасибо за прочтение…

Автор: Alexey Zuev

Источник: //blog.angularindepth.com/

Редакция: Команда webformyself.

Метки:

Похожие статьи:

Комментарии Вконтакте: