Несколько дней назадMVC, MVP, шаблон дизайна MVVM, основным механизмом MVVM является двусторонняя привязка. Двусторонняя привязка React, Vue и Angular основана на шаблоне проектирования MVVM.
Что такое двусторонняя привязка
Как показано на рисунке:
Двусторонняя привязка.jpgМеханизм двусторонней привязки поддерживает согласованность страницы (представление) и данных (данные). Сегодня MVVM является обязательной частью популярных фреймворков, описанных в предыдущем пункте.
Двусторонняя привязка в Angular2
Двусторонняя привязка также является одной из основных концепций Angular 2. Двусторонняя привязка Angular 2 выглядит следующим образом:
- data=>view: привязка данных, синтаксис шаблона []
- view=>data: привязка события, синтаксис шаблона ()
- Angular на самом деле не имеет реализации двусторонней привязки.Его двусторонняя привязка — привязка данных + привязка событий., синтаксис шаблона [()] .
Официальный пример, данный Angular2:
<!--value是数据绑定,input是事件绑定-->
<input [value]="currentHero.name"
(input)="currentHero.name=$event.target.value"
>
<!--等价-->
<input [(ngModel)]="currentHero.name">
Выше приведен синтаксис двустороннего связывания входного пространства, который ясно иллюстрирует взаимосвязь между двусторонним связыванием и двумя односторонними связываниями. здесь не используетсяngModule
грамматика,ngModule
Внутренняя реализация грамматики аналогична этой.
привязка события
- Действие пользователя вызывает уведомление о событии DOM
- Angular слушает уведомление и выполняет синтаксис шаблона.В приведенном выше примере входное значение элемента управления вводом назначается
currentHero.name
.
привязка данных
Поскольку в языке js нет механизма уведомления об изменении свойств, angular не знает, кто и когда изменился. Механизм изменений Angular:
image.pngПроцесс привязки данных для ввода в приведенном выше примере выглядит следующим образом:
- код изменен
currentHero.name
ценность . - Запускает проверку изменений для всего дерева компонентов.
- input показывает измененное значение.
Когда изменились данные
Основное внимание в следующей ситуации может изменить данные:
- Действия пользователя, такие как клики, отправки и т. д.
- Запросить данные сервера.
- временные события, такие как
setTimeout
,setInterval
.
Общим для этих точек является то, что все они асинхронны. То есть все асинхронные операции являются источником возможных изменений данных.
Как получать уведомления об изменениях
В Angularjs по коду$scope.$apply()
или$scope.$digest
триггеры, а Angular2 подключаетсяZoneJS
, который прослушивает все асинхронные события Angular. ZoneJS переписывает все асинхронные API (так называемые обезьяньи патчи, MonkeyPath). ZoneJS уведомит Angular о возможных изменениях данных, которые необходимо обнаружить для обновлений.
Принцип обнаружения изменений — грязная проверка
Так называемая грязная проверка заключается в сохранении значений всех переменных. Всякий раз, когда переменная может измениться и ее необходимо проверить, старое значение всех переменных сравнивается с новым значением. Если они не равны, это означает, что обнаружено изменение, и соответствующее представление необходимо обновить.
Разница между обнаружением изменений AngularJS и Angular2
Механизм обнаружения изменений Angularjs также является грязной проверкой, а производительность обнаружения изменений Angular2 намного лучше, чем у Angularjs.
Angular2
Ядром Angular является компонентизация, и вложение компонентов в конечном итоге образует дерево компонентов. Обнаружение изменений в Angular может выполняться в компонентах, и каждый компонент имеет соответствующий детектор изменений.ChangeDetector
. Вполне возможно, что эти детекторы изменений также образуют дерево.
Кроме того, поток данных Angular является нисходящим, односторонним потоком от родительского компонента к дочернему. Односторонний поток данных обеспечивает эффективное и предсказуемое обнаружение изменений, хотя после проверки отрицательного компонента собственный компонент может изменить данные родительского компонента так, что родительский компонент нужно будет проверять снова, что не рекомендуется при обработке данных. В режиме разработки Angular выполнит вторую проверку, и если возникнет описанная выше ситуация, вторая проверка сообщит об ошибке:ExpressionChangedAfterItHasBeenCheckedError
(Ответ на этот вопрос можно найти в Ресурсах). В производственной среде грязная проверка выполняется только один раз.
Angularjs
Напротив, Angularjs использует двусторонний поток данных, и сложный поток данных заставляет его проверять несколько раз, так что данные в конечном итоге имеют тенденцию быть стабильными. Теоретически данные никогда не могут быть стабильными, и стратегия Angularjs заключается в том, чтобы определить, что в программе есть проблема с более чем 10 грязными проверками.
angular2-change-detection-moscowjs-31-9-638.jpgОптимизация обнаружения изменений
Стратегия оптимизации
Есть 2 идеи:
- Стратегия OnPush: я знаю, что не изменился, не проверяйте меня.
- Ручное управление обновлением: я изменился, просто проверьте меня.
Стратегия обнаружения изменений OnPush
Angular также дает разработчикам возможность разрабатывать стратегии изменений.
export enum ChangeDetectionStrategy {
OnPush, // 表示变化检测对象的状态为`CheckOnce`
Default, // 表示变化检测对象的状态为`CheckAlways`
}
отChangeDetectionStrategy
Как видите, в Angular есть две стратегии обнаружения изменений.Default
Это стратегия обнаружения изменений по умолчанию в Angular, которая представляет собой грязную проверку (пока есть изменение значения, все они проверяются). Разработчики могут установить более эффективные методы обнаружения изменений в соответствии со сценарием:OnPush
.OnPush
Стратегия заключается в том, что компонент выполняет обнаружение изменений только тогда, когда изменяется ссылка на входные данные или инициируется событие.
@Component({
template: `
<h2>{{vData.name}}</h2>
<span>{{vData.email}}</span>
`,
// 设置该组件的变化检测策略为onPush
changeDetection: ChangeDetectionStrategy.OnPush
})
class VCardCmp {
@Input() vData;
}
Например, в приведенном выше примере, когдаvData
Когда значение атрибута изменяется, компонент не будет изменен обнаружением, только когдаvData
Только при переназначении. В общем, немые компоненты, которые принимают только ввод, более подходят для использования.onPush
Стратегия.
Когда изменяется ссылка на весь объект, если изменяется значение свойства объекта? Неизменяемый объект. Когда входной объект в компоненте является инвариантом, вы можете использоватьonPush
Стратегия обнаружения изменений для уменьшения частоты обнаружения изменений. Другими словами, чтобы более разумно выполнять обнаружение изменений, вы можете использоватьonPush
Стратегия.
Ручное управление обнаружением изменений
Angular не только позволяет разработчикам устанавливать стратегию обнаружения изменений, но также позволяет разработчикам получать ссылку на объект обнаружения изменений.ChangeDetectorRef
, вручную, чтобы управлять обнаружением изменений. Методы, предоставляемые ссылкой на объект обнаружения изменений для разработчика, следующие:
-
markForCheck()
: будут проверяться все родительские компоненты и все дочерние компоненты компонента, даже если для политики обнаружения изменений задано значениеonPush
. -
detach()
: Объект обнаружения изменений удаляется из дерева объектов обнаружения, и проверка изменений больше не выполняется; в сочетании сdetectChanges
Обнаружение локальных изменений может быть достигнуто. (использоватьonPush
Компоненты после политикиdetach()
неверный) -
detectChanges()
: Этот компонент и его подкомпоненты будут обнаружены в сочетании сdetach
Возможно локальное обнаружение. -
checkNoChanges()
: Обнаружение компонента и его подкомпонентов и сообщение об ошибке, если есть изменение, которое используется для проверки того, что изменение было завершено на этапе разработки. -
reattach()
: повторно связать отсоединенные объекты обнаружения изменений с деревом обнаружения изменений.
Затем, если это Observable, он будет подписываться на все изменения переменных, пока обнаружение изменений запускается вручную в функции обратного вызова подписки для достижения обнаружения минимальной стоимости (все еще с использованиемonPush
стратегия обнаружения изменений). Например:
@Component({
template: '{{counter}}',
changeDetection: ChangeDetectionStrategy.OnPush
})
class CartBadgeCmp {
@Input() addItemStream:Observable<any>;
counter = 0;
constructor(private cd: ChangeDetectorRef) {}
ngOnInit() {
this.addItemStream.subscribe(() => {
this.counter++; // 数据模型发生变化
this.cd.markForCheck(); // 手动触发检测
})
}
}
Кроме того, когда модель данных меняется слишком часто, мы можем настроить время обнаружения изменений. Например:
@Component({
template: `{{counter}}
<input type="check" (click)="toggle()">`,
})
class CartBadgeCmp {
counter = 0;
detectEnabled = false;
constructor(private cd: ChangeDetectorRef) {}
ngOnInit() {
// 每10毫秒增加1
setInterval(()=>{this.counter++}, 10);
}
toggle(){
if( this.detectEnabled ){
this.cd.reattach(); // 链接上变化检测树
}
else{
this.cd.detach(); // 脱离变化检测树
}
}
}
Суммировать
Как угловые, так и angularjs используют механизмы обнаружения изменений. Первый превосходит последнее в основном в:
- единый поток данных
- Независимое от размеров обнаружение в составных блоках
- Рабочая среда проверяется только один раз
- Настраиваемая стратегия обнаружения изменений:
Default
а такжеonPush
- Настраиваемые действия по обнаружению изменений:
markForcheck()
,detectChanges()
,detach()
,reattach()
,checkNoChanges()
- Говорят, что оптимизация реализации кода использует дружественный к VM код.