Wykrywanie zmiany kątowej i strategia OnPush

Opublikowany: 2022-03-11

Zacząłeś używać Angulara we wszystkich swoich ulubionych projektach. Wiesz, co oferuje Angular i jak możesz go wykorzystać do tworzenia niesamowitych aplikacji internetowych. Ale są pewne rzeczy związane z Angularem, a ich znajomość może sprawić, że będziesz lepiej posługiwać się Angularem w swoich projektach.

Przepływ danych będący w centrum prawie wszystkich rzeczy związanych z Angularem, wykrywanie zmian jest czymś, o czym warto wiedzieć, ponieważ pomoże ci to znacznie łatwiej śledzić błędy i da ci możliwość dalszej optymalizacji aplikacji podczas pracy ze złożonym zestawem danych.

Wykrywanie zmiany kątowej i strategia OnPush

W tym artykule dowiesz się, jak Angular wykrywa zmiany w swoich strukturach danych i jak możesz uczynić je niezmiennymi, aby jak najlepiej wykorzystać strategie wykrywania zmian Angulara.

Wykrywanie zmian w Angular

Kiedy zmienisz którykolwiek z twoich modeli, Angular wykrywa zmiany i natychmiast aktualizuje widoki. To jest wykrywanie zmian w Angularze. Celem tego mechanizmu jest upewnienie się, że podstawowe widoki są zawsze zsynchronizowane z odpowiadającymi im modelami. Ta podstawowa funkcja Angulara jest tym, co sprawia, że ​​framework działa i jest częściowo powodem, dla którego Angular jest dobrym wyborem do tworzenia nowoczesnych aplikacji internetowych.

Model w Angularze może się zmienić w wyniku dowolnego z następujących scenariuszy:

  • Zdarzenia DOM (kliknij, najedź kursorem itp.)

  • Żądania AJAX

  • Zegary (setTimer(), setInterval())

Zmień detektory

Wszystkie aplikacje Angular składają się z hierarchicznego drzewa komponentów. W czasie wykonywania Angular tworzy osobną klasę detektora zmian dla każdego komponentu w drzewie, która ostatecznie tworzy hierarchię detektorów zmian podobną do hierarchicznego drzewa komponentów.

Za każdym razem, gdy wyzwalane jest wykrywanie zmian, Angular przechodzi w dół tego drzewa detektorów zmian, aby określić, czy którykolwiek z nich zgłosił zmiany.

Cykl wykrywania zmian jest zawsze wykonywany raz dla każdej wykrytej zmiany i rozpoczyna się od detektora zmian głównych i przechodzi w dół w sposób sekwencyjny. Ten sekwencyjny wybór projektu jest dobry, ponieważ aktualizuje model w przewidywalny sposób, ponieważ wiemy, że dane komponentów mogą pochodzić tylko od jego rodzica.

Zmień hierarchię detektorów

Detektory zmian umożliwiają śledzenie poprzednich i bieżących stanów komponentu, a także jego struktury w celu zgłaszania zmian do Angulara.

Jeśli Angular otrzyma raport z detektora zmian, nakazuje odpowiedniemu komponentowi ponowne renderowanie i odpowiednią aktualizację DOM.

Strategie wykrywania zmian

Wartość a typy referencyjne

Aby zrozumieć, czym jest strategia wykrywania zmian i dlaczego działa, musimy najpierw zrozumieć różnicę między typami wartości i typami referencyjnymi w JavaScript. Jeśli już wiesz, jak to działa, możesz pominąć tę sekcję.

Na początek przejrzyjmy typy wartości i typy odwołań oraz ich klasyfikacje.

Typy wartości

  • Boole'a

  • Zero

  • Nieokreślony

  • Numer

  • Strunowy

Dla uproszczenia można sobie wyobrazić, że te typy po prostu przechowują swoją wartość w pamięci stosu (co technicznie nie jest prawdą, ale wystarczy w tym artykule). Zobacz na przykład pamięć stosu i jej wartości na poniższym obrazku.

Pamięć stosu

Typy referencyjne

  • Tablice

  • Obiekty

  • Funkcje

Te typy są nieco bardziej skomplikowane, ponieważ przechowują referencje w pamięci stosu, co wskazuje na ich rzeczywistą wartość w pamięci sterty. Możesz zobaczyć, jak pamięć stosu i pamięć sterty współpracują ze sobą na przykładowym obrazie poniżej. Widzimy, że pamięć stosu odwołuje się do rzeczywistych wartości typu referencyjnego w pamięci sterty.

Pamięć stosu i pamięć stosu

Ważnym rozróżnieniem między typami wartości a typami referencyjnymi jest to, że aby odczytać wartość typu wartości, wystarczy odpytać pamięć stosu, ale aby odczytać wartość typu referencyjnego, musimy najpierw zapytaj o pamięć stosu, aby uzyskać odwołanie, a następnie użyj tego odwołania do zbadania pamięci stosu, aby zlokalizować wartość typu odwołania.

Domyślna strategia

Jak wspomnieliśmy wcześniej, Angular monitoruje zmiany w modelu, aby upewnić się, że wychwytuje wszystkie zmiany. Sprawdzi wszelkie różnice między poprzednim stanem a bieżącym stanem całego modelu aplikacji.

Pytanie, które zadaje Angular w domyślnej strategii wykrywania zmian, brzmi: Czy zmieniła się jakaś wartość w modelu? Ale w przypadku typu referencyjnego możemy wdrożyć strategie, abyśmy mogli zadać lepsze pytanie. W tym miejscu pojawia się strategia wykrywania zmian OnPush.

Strategia OnPush

Główna idea strategii OnPush wynika z uświadomienia sobie, że jeśli traktujemy typy referencyjne jako niezmienne obiekty, możemy wykryć, czy wartość zmieniła się znacznie szybciej. Gdy typ odniesienia jest niezmienny, oznacza to, że za każdym razem, gdy jest aktualizowany, odniesienie w pamięci stosu będzie musiało ulec zmianie. Teraz możemy po prostu sprawdzić: Czy zmieniła się referencja (w stosie) typu referencji? Jeśli tak, tylko wtedy sprawdź wszystkie wartości (na stosie). Odwołaj się do poprzednich diagramów stosu, jeśli jest to mylące.

Strategia OnPush w zasadzie zadaje dwa pytania zamiast jednego. Czy zmieniło się odniesienie do typu odwołania? Jeśli tak, to czy zmieniły się wartości w pamięci sterty?

Załóżmy na przykład, że mamy niezmienną tablicę z 30 elementami i chcemy wiedzieć, czy są jakieś zmiany. Wiemy, że aby istniały jakiekolwiek aktualizacje niezmiennej tablicy, odniesienie do niej (na stosie) musiałoby się zmienić. Oznacza to, że możemy początkowo sprawdzić, czy odwołanie do tablicy jest inne, co potencjalnie uchroniłoby nas przed wykonaniem kolejnych 30 sprawdzeń (w stercie) w celu ustalenia, który element jest inny. Nazywa się to strategią OnPush.

Możesz więc zapytać, co to znaczy traktować typy referencyjne jako niezmienne? Oznacza to, że nigdy nie ustawiamy właściwości typu referencyjnego, ale zamiast tego ponownie przypisujemy wartość razem. Zobacz poniżej:

Traktowanie obiektów jako zmiennych:

 static mutable() { var before = {foo: "bar"}; var current = before; current.foo = "hello"; console.log(before === current); // => true }

Traktowanie obiektów jako niezmienne:

 static mutable() { var before = {foo: "bar"}; var current = before; current = {foo "hello"}; console.log(before === current); // => false }

Zauważ, że w powyższych przykładach „traktujemy” typy referencyjne jako niezmienne zgodnie z konwencją, więc ostatecznie nadal pracujemy z obiektami mutowalnymi, ale po prostu „udając”, że są niezmienne.

Jak więc wdrożyć strategię OnPush dla komponentu? Wszystko, co musisz zrobić, to dodać parametr changeDetection do ich adnotacji @Component.

 import {ChangeDetectionStrategy, Component} from '@angular/core'; @Component({ // ... changeDetection: ChangeDetectionStrategy.OnPush }) export class OnPushComponent { // ... }

Niezmienny.js

Dobrym pomysłem jest wymuszenie niezmienności, jeśli zdecydujemy się na użycie strategii OnPush na komponencie Angular. Tu właśnie pojawia się Immutable.js.

Immutable.js to biblioteka stworzona przez Facebooka dla niezmienności w JavaScript. Mają wiele niezmiennych struktur danych, takich jak List, Map i Stack. Na potrzeby tego artykułu zostaną zilustrowane Lista i Mapa. Aby uzyskać więcej informacji, zapoznaj się z oficjalną dokumentacją tutaj.

Aby dodać Immutable.js do swoich projektów, wejdź do terminala i uruchom:

 $ npm install immutable --save

Pamiętaj również o zaimportowaniu używanych struktur danych z pliku Immutable.js w komponencie, w którym go używasz.

 import {Map, List} from 'immutable';

Oto jak można użyć mapy Immutable.js:

 var foobar = {foo: "bar"}; var immutableFoobar = Map(foobar); console.log(immutableFooter.get("foo")); // => bar

A tablica może być użyta:

 var helloWorld = ["Hello", "World!"]; var immutableHelloWorld = List(helloWorld); console.log(immutableHelloWorld.first()); // => Hello console.log(immutableHelloWorld.last()); // => World! helloWorld.push("Hello Mars!"); console.log(immutableHelloWorld.last()); // => Hello Mars!

Wady używania Immutable.js

Istnieje kilka głównych dyskusyjnych wad używania Immutable.js.

Jak mogłeś zauważyć, korzystanie z jego interfejsu API jest nieco kłopotliwe, a tradycyjny programista JavaScript może tego nie lubić. Poważniejszy problem wiąże się z brakiem możliwości implementacji interfejsów dla modelu danych, ponieważ Immutable.js nie obsługuje interfejsów.

Zakończyć

Możesz zapytać, dlaczego strategia OnPush nie jest domyślną strategią dla Angulara. Przypuszczam, że dzieje się tak dlatego, że Angular nie chciał zmuszać programistów JavaScript do pracy z niezmiennymi obiektami. Ale to nie znaczy, że nie możesz go używać.

Jeśli jest to coś, co chcesz wykorzystać w swoim następnym projekcie internetowym, teraz wiesz, jak łatwo Angular umożliwia przejście na inną strategię wykrywania zmian.