Jak działa wiązanie danych w AngularJS
ramach?
Nie znalazłem szczegółów technicznych na ich stronie . Mniej lub bardziej jasne jest, jak to działa, gdy dane są propagowane z widoku do modelu. Ale w jaki sposób AngularJS śledzi zmiany właściwości modelu bez ustawień i getterów?
Odkryłem, że istnieją obserwatorzy JavaScript, którzy mogą wykonywać tę pracę. Ale nie są obsługiwane w Internet Explorer 6 i Internet Explorer 7 . Skąd więc AngularJS wie, że zmieniłem na przykład następujące i odzwierciedliłem tę zmianę w widoku?
myobject.myproperty="new value";
javascript
angularjs
data-binding
Pashec
źródło
źródło
Odpowiedzi:
AngularJS zapamiętuje wartość i porównuje ją z poprzednią wartością. To jest podstawowa kontrola brudu. Jeśli nastąpi zmiana wartości, następuje uruchomienie zdarzenia zmiany.
$apply()
Metoda, która jest co nazywasz podczas przechodzenia z non-angularjs świata w na świecie angularjs, połączeń$digest()
. Podsumowanie to po prostu stara, brudna kontrola. Działa na wszystkich przeglądarkach i jest całkowicie przewidywalny.Aby skontrolować brudne sprawdzanie (AngularJS) w porównaniu ze zmieniającymi się odbiornikami ( KnockoutJS i Backbone.js ): Chociaż brudne sprawdzanie może wydawać się proste, a nawet nieefektywne (zajmę się tym później), okazuje się, że jest ono poprawne semantycznie przez cały czas, podczas gdy nasłuchiwanie zmian ma wiele dziwnych przypadków narożnych i potrzebuje takich rzeczy, jak śledzenie zależności, aby było to poprawniejsze semantycznie. Śledzenie zależności KnockoutJS to sprytna funkcja dla problemu, którego nie ma AngularJS.
Problemy z detektorami zmian:
Co z wydajnością?
Może się więc wydawać, że jesteśmy spowolnieni, ponieważ brudne sprawdzanie jest nieskuteczne. Tutaj musimy przyjrzeć się liczbom rzeczywistym, a nie tylko argumentom teoretycznym, ale najpierw zdefiniujmy pewne ograniczenia.
Ludzie to:
Powolny - wszystko, co jest szybsze niż 50 ms, jest niedostrzegalne dla ludzi i dlatego można je uznać za „natychmiastowe”.
Ograniczony - naprawdę nie można pokazać więcej niż około 2000 informacji na jednej stronie. Wszystko inne to naprawdę zły interfejs użytkownika, a ludzie i tak nie mogą tego przetworzyć.
Prawdziwe pytanie brzmi zatem: ile porównań można wykonać w przeglądarce w ciągu 50 ms? Trudno odpowiedzieć na to pytanie, ponieważ w grę wchodzi wiele czynników, ale tutaj jest przypadek testowy: http://jsperf.com/angularjs-digest/6, który tworzy 10 000 obserwatorów. W nowoczesnej przeglądarce zajmuje to nieco mniej niż 6 ms. W przeglądarce Internet Explorer 8 zajmuje to około 40 ms. Jak widać, nie jest to obecnie problemem nawet w przypadku wolnych przeglądarek. Jest zastrzeżenie: porównania muszą być proste, aby zmieścić się w limicie czasu ... Niestety, zbyt wolno jest dodawać powolne porównanie do AngularJS, więc łatwo jest budować wolne aplikacje, gdy nie wiesz, co robią. Mamy jednak nadzieję, że uzyskamy odpowiedź, dostarczając moduł oprzyrządowania, który pokaże, jakie są powolne porównania.
Okazuje się, że gry wideo i procesory graficzne wykorzystują podejście „brudnego sprawdzania”, szczególnie dlatego, że jest spójne. Tak długo, jak przekraczają częstotliwość odświeżania monitora (zwykle 50-60 Hz lub co 16,6-20 ms), każda nadwyżka wydajności jest marnotrawstwem, więc lepiej jest rysować więcej rzeczy, niż zwiększać FPS.
źródło
Misko przedstawił już doskonały opis działania powiązań danych, ale chciałbym dodać mój pogląd na problem z wydajnością powiązania danych.
Jak stwierdził Misko, około 2000 powiązań zaczyna powodować problemy, ale i tak nie powinno być więcej niż 2000 informacji na stronie. Może to być prawda, ale nie każde powiązanie danych jest widoczne dla użytkownika. Gdy zaczniesz budować dowolny rodzaj widżetu lub siatki danych z dwukierunkowym wiązaniem, możesz łatwo trafić w 2000 powiązań bez złego UX.
Rozważmy na przykład pole kombi, w którym można wpisać tekst, aby przefiltrować dostępne opcje. Ten rodzaj kontroli może zawierać ~ 150 przedmiotów i nadal może być bardzo użyteczny. Jeśli ma jakąś dodatkową funkcję (na przykład określoną klasę w aktualnie wybranej opcji), zaczynasz otrzymywać 3-5 wiązań na opcję. Umieść trzy z tych widżetów na stronie (np. Jeden, aby wybrać kraj, drugi, aby wybrać miasto we wspomnianym kraju, a trzeci, aby wybrać hotel), a już masz około 1000 do 2000 powiązań.
Lub rozważ siatkę danych w korporacyjnej aplikacji internetowej. 50 wierszy na stronie nie jest nierozsądne, z których każdy może mieć 10-20 kolumn. Jeśli zbudujesz to za pomocą powtórzeń ng i / lub masz informacje w niektórych komórkach, które używają niektórych powiązań, możesz zbliżyć się do 2000 powiązań z samą siatką.
Uważam, że jest to ogromny problem podczas pracy z AngularJS, a jedynym rozwiązaniem, jakie udało mi się znaleźć do tej pory, jest konstruowanie widgetów bez użycia dwukierunkowego wiązania, zamiast używania ngOnce, wyrejestrowywania obserwatorów i podobnych sztuczek, lub konstruowania dyrektywy budujące DOM za pomocą jQuery i manipulacji DOM. Wydaje mi się, że w pierwszej kolejności jest to sprzeczne z celem używania Angulara.
Chciałbym usłyszeć sugestie dotyczące innych sposobów rozwiązania tego problemu, ale może powinienem napisać własne pytanie. Chciałem to skomentować, ale okazało się, że jest na to za długo ...
TL; DR
Powiązanie danych może powodować problemy z wydajnością na złożonych stronach.
źródło
Brudne sprawdzanie
$scope
obiektuAngular utrzymuje prostą
array
obserwację$scope
obiektów. Jeśli sprawdzisz jakiś$scope
, okaże się, że zawiera onarray
wywołanie$$watchers
.Każdy obserwator
object
zawiera między innymiattribute
nazwa lub coś bardziej skomplikowanego.$scope
jako brudny.Jak definiowani są obserwatorzy
Istnieje wiele różnych sposobów definiowania obserwatora w AngularJS.
Można wyraźnie ty na .
$watch
attribute
$scope
Możesz umieścić
{{}}
interpolację w swoim szablonie (dla bieżącego zostanie utworzony obserwator$scope
).Możesz poprosić o dyrektywę, taką jak
ng-model
zdefiniowanie obserwatora dla ciebie.$digest
Cykl sprawdza wszystkie obserwatorów przed ich ostatniej wartościKiedy wchodzimy w interakcję z AngularJS poprzez normalne kanały (model ng, powtórzenie ng itp.), Dyrektywa uruchomi cykl podsumowania.
Cykl trawienia to pierwsze przejście w głąb
$scope
i wszystkich jego dzieci . Dla każdego z nich$scope
object
iterujemy$$watchers
array
i oceniamy wszystkie wyrażenia. Jeśli nowa wartość wyrażenia różni się od ostatniej znanej wartości, wywoływana jest funkcja obserwatora. Ta funkcja może ponownie skompilować część DOM, ponownie obliczyć wartość$scope
, wyzwolićAJAX
request
dowolne potrzebne zadanie.Każdy zakres jest przeglądany, a każde wyrażenie obserwowane jest oceniane i sprawdzane względem ostatniej wartości.
Jeśli obserwator zostanie wyzwolony,
$scope
jest brudnyJeśli obserwator zostanie wyzwolony, aplikacja wie, że coś się zmieniło i
$scope
jest oznaczony jako brudny.Funkcje obserwatora mogą zmieniać inne atrybuty na
$scope
lub na rodzicu$scope
. Jeśli jedna$watcher
funkcja została uruchomiona, nie możemy zagwarantować, że nasze pozostałe$scope
są nadal czyste, dlatego ponownie wykonujemy cały cykl podsumowania.Wynika to z faktu, że AngularJS ma dwukierunkowe wiązanie, dzięki czemu dane mogą być przekazywane z powrotem do
$scope
drzewa. Możemy zmienić wartość wyższą,$scope
która została już przetworzona. Być może zmienimy wartość na$rootScope
.Jeśli
$digest
jest brudny,$digest
ponownie wykonujemy cały cyklCiągle zapętlamy
$digest
cykl, aż albo cykl podsumowania nie stanie się czysty (wszystkie$watch
wyrażenia mają taką samą wartość jak w poprzednim cyklu), lub osiągniemy limit podsumowania. Domyślnie limit ten wynosi 10.Jeśli osiągniemy limit skrótu, AngularJS zgłosi błąd w konsoli:
Podsumowanie jest trudne na komputerze, ale łatwe dla programisty
Jak widać, za każdym razem, gdy coś zmienia się w aplikacji AngularJS, AngularJS sprawdzi każdego obserwatora w
$scope
hierarchii, aby zobaczyć, jak zareagować. Dla programisty jest to ogromny dar wydajności, ponieważ teraz nie musisz pisać prawie żadnego kodu okablowania, AngularJS po prostu zauważy, czy wartość się zmieniła, i sprawi, że reszta aplikacji będzie zgodna ze zmianą.Z punktu widzenia maszyny jest to jednak bardzo nieefektywne i spowolni naszą aplikację, jeśli utworzymy zbyt wielu obserwatorów. Misko podało liczbę około 4000 obserwatorów, zanim Twoja aplikacja będzie działać wolniej w starszych przeglądarkach.
Ten limit jest łatwo osiągalny, jeśli na przykład
ng-repeat
przekraczasz dużą wartośćJSON
array
. Można temu zaradzić, korzystając z takich funkcji, jak jednorazowe wiązanie w celu skompilowania szablonu bez tworzenia obserwatorów.Jak uniknąć tworzenia zbyt wielu obserwatorów
Za każdym razem, gdy użytkownik wchodzi w interakcję z aplikacją, każdy obserwator w Twojej aplikacji zostanie oceniony co najmniej raz. Dużą częścią optymalizacji aplikacji AngularJS jest zmniejszenie liczby obserwatorów w twoim
$scope
drzewie. Jednym łatwym sposobem na to jest wiązanie jednorazowe .Jeśli masz dane, które rzadko się zmieniają, możesz je powiązać tylko raz, używając składni ::, tak jak:
lub
Powiązanie zostanie uruchomione tylko wtedy, gdy szablon zawierający zostanie wyrenderowany i dane zostaną załadowane
$scope
.Jest to szczególnie ważne, gdy masz
ng-repeat
wiele przedmiotów.źródło
To jest moje podstawowe zrozumienie. To może być źle!
$watch
metody.$apply
metodą$apply
tej$digest
metoda jest wywoływana, który przechodzi każdy z zegarków i sprawdza, czy one zmianie od ostatniej chwili$digest
uciekł.W normalnym rozwoju składnia powiązania danych w HTML mówi kompilatorowi AngularJS, aby utworzył dla ciebie zegarki, a metody kontrolera są
$apply
już uruchomione . Dla programisty aplikacji wszystko jest przejrzyste.źródło
Zastanawiałem się przez chwilę. Bez setterów jak
AngularJS
zauważają zmiany w$scope
obiekcie? Czy to sonduje je?W rzeczywistości robi to: każde „normalne” miejsce, w którym modyfikujesz model, było już wywoływane z trzewi
AngularJS
, więc automatycznie wywołuje$apply
cię po uruchomieniu kodu. Powiedzmy, że twój kontroler ma metodę, która jest podłączona dong-click
jakiegoś elementu. PonieważAngularJS
łączysz wywoływanie tej metody razem, masz szansę zrobić to$apply
w odpowiednim miejscu. Podobnie, dla wyrażeń pojawiających się bezpośrednio w widokach, są one wykonywane przez,AngularJS
więc robi to$apply
.Kiedy dokumentacja mówi o konieczności
$apply
ręcznego wywoływania kodu pozaAngularJS
, mówi o kodzie, który po uruchomieniu nie wynika zAngularJS
samego siebie na stosie wywołań.źródło
Objaśnienie ze zdjęciami:
Powiązanie danych wymaga mapowania
Odwołanie w zakresie nie jest dokładnie odwołaniem w szablonie. Podczas wiązania dwóch obiektów potrzebny jest trzeci, który nasłuchuje pierwszego i zmodyfikuje drugi.
Tutaj, po zmodyfikowaniu
<input>
, dotkniesz data-ref3 . A klasyczny mechanizm wiązania danych zmieni dane ref4 . Jak poruszą się inne{{data}}
wyrażenia?Zdarzenia prowadzą do $ digest ()
Kątowe utrzymuje
oldValue
inewValue
od każdego wiążące. I po każdym wydarzeniu$digest()
Angulara słynna pętla sprawdzi WatchList, aby sprawdzić, czy coś się zmieniło. Te kątowe zdarzenia sąng-click
,ng-change
,$http
zakończone ... The$digest()
pętla będzie tak długo, jak każdyoldValue
różni się odnewValue
.Na poprzednim zdjęciu zauważysz, że data-ref1 i data-ref2 uległy zmianie.
Wnioski
To trochę jak jajko i kurczak. Nigdy nie wiadomo, kto zaczyna, ale mam nadzieję, że przez większość czasu działa zgodnie z oczekiwaniami.
Drugą kwestią jest to, że można łatwo zrozumieć głębokie oddziaływanie prostego wiązania na pamięć i procesor. Mam nadzieję, że komputery stacjonarne są wystarczająco grube, aby sobie z tym poradzić. Telefony komórkowe nie są tak silne.
źródło
Oczywiście nie ma okresowej kontroli,
Scope
czy istnieje jakaś zmiana w dołączonych do niej obiektach. Nie wszystkie obiekty dołączone do lunety są obserwowane. Zakres prototypowo utrzymuje obserwatorów $$ .Scope
tylko iteruje przez to,$$watchers
kiedy$digest
jest wywoływany.Angular dodaje obserwatora do obserwatorów $$ dla każdego z nich
Funkcja $ watch przyjmuje trzy parametry:
W Angular jest interesująca rzecz o nazwie Cykl trawienny. Cykl $ digest rozpoczyna się w wyniku wywołania $ scope. $ Digest (). Załóżmy, że zmieniasz model $ scope w funkcji modułu obsługi poprzez dyrektywę ng-click. W takim przypadku AngularJS automatycznie uruchamia cykl $ digest przez wywołanie $ digest (). Oprócz ng-click istnieje kilka innych wbudowanych dyrektyw / usług, które pozwalają zmieniać modele (np. Ng-model, $ limit czasu itp.) i automatycznie uruchamia cykl $ streszczenia. Z grubsza implementacja $ digest wygląda następująco.
Jeśli użyjemy funkcji setTimeout () JavaScript do aktualizacji modelu zakresu, Angular nie będzie wiedział, co możesz zmienić. W takim przypadku naszym obowiązkiem jest ręczne wywołanie $ Apply (), co uruchomi cykl skrótu $. Podobnie, jeśli masz dyrektywę, która konfiguruje detektor zdarzeń DOM i zmienia niektóre modele wewnątrz funkcji modułu obsługi, musisz wywołać $ apply (), aby zmiany zostały wprowadzone. Główną ideą $ Apply jest to, że możemy wykonać kod, który nie jest świadomy Angulara, a kod ten może wciąż zmieniać rzeczy w zakresie. Jeśli zawiniemy ten kod w $ apply, zajmie się wywołaniem $ digest (). Szorstka implementacja $ apply ().
źródło
AngularJS obsługuje mechanizm wiązania danych za pomocą trzech potężnych funkcji: $ watch () , $ digest () i $ apply () . Przez większość czasu AngularJS wywołuje $ scope. $ Watch () i $ scope. $ Digest (), ale w niektórych przypadkach może być konieczne ręczne wywołanie tych funkcji w celu zaktualizowania o nowe wartości.
$ watch () : -
$ digest () -
Zastosuj $ () -
źródło
Zdarzyło się, że musiałem połączyć model danych osoby z formularzem, a zrobiłem bezpośrednie mapowanie danych z formularzem.
Na przykład, jeśli model miał coś takiego:
Kontrolne wejście formularza:
W ten sposób, jeśli zmodyfikujesz wartość kontrolera obiektu, zostanie to automatycznie odzwierciedlone w widoku.
Przykładem, w którym zdałem, że model jest aktualizowany z danych serwera, jest prośba o kod pocztowy i kod pocztowy oparty na zapisanych obciążeniach, listę kolonii i miast powiązanych z tym widokiem i domyślnie ustawia pierwszą wartość dla użytkownika. I to działało bardzo dobrze, co się dzieje, że
angularJS
czasami odświeżenie modelu zajmuje kilka sekund, aby to zrobić, możesz umieścić pokrętło podczas wyświetlania danych.źródło
Powiązanie danych w jedną stronę to podejście, w którym wartość jest pobierana z modelu danych i wstawiana do elementu HTML. Nie ma możliwości zaktualizowania modelu z widoku. Jest stosowany w klasycznych systemach szablonów. Systemy te wiążą dane tylko w jednym kierunku.
Powiązanie danych w aplikacjach Angular to automatyczna synchronizacja danych między modelem a komponentami widoku.
Powiązanie danych pozwala traktować model jako jedyne źródło prawdy w aplikacji. Widok jest zawsze rzutem modelu. Jeśli model zostanie zmieniony, widok odzwierciedla zmianę i odwrotnie.
źródło
Oto przykład powiązania danych z AngularJS przy użyciu pola wejściowego. Wyjaśnię później
Kod HTML
Kod AngularJS
Jak widać w powyższym przykładzie, AngularJS używa
ng-model
do nasłuchiwania i obserwowania, co dzieje się z elementami HTML, zwłaszcza nainput
polach. Kiedy coś się stanie, zrób coś. W naszym przypadkung-model
wiąże się z naszym widokiem przy użyciu zapisu wąsów{{}}
. Cokolwiek wpisane jest w polu wprowadzania, jest natychmiast wyświetlane na ekranie. Na tym polega piękno wiązania danych przy użyciu AngularJS w najprostszej formie.Mam nadzieję że to pomoże.
Zobacz działający przykład tutaj na Codepen
źródło
AngularJs obsługuje dwukierunkowe wiązanie danych .
Oznacza dostęp do danych Widok -> Kontroler i kontroler -> Widok
Dla np.
1)
O / P
Możesz powiązać dane w
ng-model
polubieniu: -2)
Tutaj w powyższym przykładzie, cokolwiek poda użytkownik, będzie widoczne w
<div>
znaczniku.Jeśli chcesz powiązać dane wejściowe z HTML do kontrolera: -
3)
Tutaj, jeśli chcesz użyć danych wejściowych
name
w kontrolerze,ng-model
wiąże nasz pogląd i wyraża go w wyrażeniu{{ }}
.ng-model
to dane pokazane użytkownikowi w widoku i z którymi użytkownik wchodzi w interakcje.Więc łatwo jest powiązać dane w AngularJs.
źródło
Angular.js tworzy obserwator dla każdego modelu, który tworzymy w widoku. Za każdym razem, gdy model jest zmieniany, do modelu dołączana jest klasa „brudny”, więc obserwator obserwuje wszystkie modele, które mają klasę „brudny” i aktualizuje ich wartości w kontrolerze i odwrotnie.
źródło
wiązanie danych:
Co wiąże dane?
Za każdym razem, gdy użytkownik zmienia dane w widoku, następuje aktualizacja tej zmiany w modelu zakresu i odwrotnie.
Jak to jest możliwe?
Krótka odpowiedź: za pomocą cyklu trawienia.
Opis: Angular js ustawia obserwatora na modelu zakresu, który uruchamia funkcję detektora, jeśli nastąpi zmiana w modelu.
// Kod aktualizacji Dom z nową wartością
});
Kiedy i jak wywoływana jest funkcja obserwatora?
Funkcja obserwatora jest wywoływana jako część cyklu podsumowania.
Cykl trawienia jest wywoływany automatycznie jako część kątowych js wbudowanych w dyrektywy / usługi, takie jak ng-model, ng-bind, $ timeout, ng-click i inne .., które pozwalają uruchomić cykl podsumowania.
Funkcja cyklu trawienia:
to znaczy
$rootScope.$apply()
Uwaga: $ Apply () jest równe $ rootScope. $ Digest () oznacza, że brudne sprawdzanie rozpoczyna się od katalogu głównego lub góry lub zakresu nadrzędnego aż do wszystkich potomnych zakresów $ w aplikacji kątowej js.
Powyższe funkcje działają w przeglądarkach IE dla wspomnianych wersji także po prostu upewniając się, że twoja aplikacja jest aplikacją angs js, co oznacza, że używasz pliku skryptu frameworku angularjs, do którego odwołuje się znacznik script.
Dziękuję Ci.
źródło