jako programista zawsze chcesz zoptymalizować swój kod. Obejmuje to szybkość, z jaką prezentujesz użytkownikowi w pełni załadowany interfejs użytkownika, który często zależy od danych pochodzących z bazy danych.
nieuchronnie zaczynasz szukać sposobów na rozwiązanie danych natychmiast po przejściu do nowej strony / obszaru aplikacji, bez napotkania przez użytkownika czegoś, co jest znane jako page JANK.
To jest, gdy strona porusza się w górę iw dół podczas ładowania niektórych komponentów. Może znacząco wpłynąć na UX do punktu, w którym wygląda na „buggy”.
Angular zapewnia intuicyjne podejście do wstępnego pobierania danych przed załadowaniem trasy; przed usunięciem nawigowanej trasy.
nazywa się to Resolver kątowy.
Rozdzielczość kątowa jest zasadniczo usługą kątową. Klasa iniekcji, którą podajesz do modułu routingu w konfiguracji trasy. Ten specjalny rodzaj usługi jest wstrzykiwany i wykonywany, gdy trasa zawierająca jest nawigowana do.
Resolver następnie rozwiązuje dane przed załadowaniem strony, które stają się dostępne za pośrednictwem usługi ActivatedRoute
. Zapewnia to prosty i wydajny sposób zapewnienia, że użytkownik ma DANE tak szybko, jak to możliwe, zanim komponent, który jest ważny dla początkowego ładowania strony, będzie go potrzebował.
innym sposobem użycia Resolvera kątowego jest użycie go jako metody natychmiastowego wypełniania metadanych SEO.
dzięki resolverowi masz gwarancję, że dane będą istniały przed załadowaniem strony, zapewniając, że wszystko ma to, czego potrzebuje podczas inicjalizacji.
- Podzielmy rozdzielczość kątową
- Krok 1: Tworząc Resolver
- Adnotacja klasy Resolver profilu za pomocą dekoratora do wstrzykiwania
- pobieranie danych z bazy danych
- Bonus: Szybko uzupełniaj dynamiczne metadane SEO, zanim trasa zostanie załadowana
- Krok 2: wstrzyknięcie resolvera do modułu routingu
- Krok 3: Zainicjalizuj komponent z rozwiązanymi danymi
- co natychmiast dostępne dane robią dla komponentu
- istnieje wiele sposobów na to, że komponent może załadować fragmentowany
- to jest to! Mamy resolver z ulepszonym UX przy ładowaniu strony.
Podzielmy rozdzielczość kątową
Rozdzielczość kątowa jest klasą, która implementuje interfejsResolve
. InterfejsResolve
wymaga implementacji funkcji w klasie o nazwie resolve
.
Oto rozwiązywanie podpisu interfejsu…
export interface Resolve<T> {
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<T> | Promise<T> | T {
return 'data';
}
}
jak widać z podpisu interfejsu, wymaga on ogólnego argumentu
T
, który będzie typem naszych rozwiązanych danych.
funkcja resolve zwracaObservable
Promise
lub tylko dane typuT
. Dlatego też istnieje sugestia, że powinno to być obsługiwane asynchronicznie, zwłaszcza w przypadku pobierania danych z bazy danych.
głównym celem funkcji resolve jest jej ukończenie. Fakt ten należy pamiętać podczas pobierania danych w observables. Obserwowalny musi się zakończyć. W końcu jest to resolver.
Jeśli obserwowalny nie zakończy się, dane nigdy nie zostaną rozwiązane, a strona nigdy się nie załaduje. Dlatego musisz zdefiniować punkt, w którym nie musisz przyjmować żadnych więcej wartości, a dane mogą zostać rozwiązane, ponieważ masz wszystko, czego potrzebujesz z bazy danych. Podczas korzystania z asynchronicznych strumieni danych, takich jak observables, jest to przypadek użycia dla pipeable operatorów z RXJS.
operatory pipeable, które przychodzą na myśl, gdy myślisz o ukończeniu strumienia danych na podstawie warunku, są kombinacjąfilter
take
first
. Dzięki tej kombinacji możesz filtrować wszystkie wartości, których nie chcesz przyjmować, takie jak null
lub undefined
lub pusta tablica , następnie
take
pierwsza poprawna wartość z take
take(1)
.
operatory, które mogą być potrzebne do wczesnego zakończenia obserwacji w przypadku napotkania problemów lub błędów, w których chcesz zwrócić null lub redirect, tocatchError
Itimeout
. Kombinacja timeout
I catchError
jest przydatna, jeśli Twoje dane trwają zbyt długo i chcesz zwrócić null
, aby móc spróbować ponownie wewnątrz komponentu lub przekierować.
Jeśli Twoje dane nie rozwiązują się szybko, są oparte na złożonym filtrowaniu, logice, ogromnej liczbie wywołań bazy danych, prawdopodobnie od czasu do czasu pojawią się problemy.
najlepiej określić najmniejszą ilość wywołań bazy danych i minimalną ilość danych potrzebnych do pomyślnego i wdzięcznego załadowania strony.
w związku z tym, możesz poświęcić trochę czasu, zanim zaimplementujesz swój resolver, aby skupić się na oddzieleniu zawartości „powyżej zagięcia” od danych, które mogą być załadowane podczas inicjalizacji strony.
dlatego możesz podzielić dane niezbędne do płynnego UX, od reszty danych, które mogą być wywołane z komponentu, a nie z resolvera.
możesz wtedy uporać się wyłącznie z minimalną, ponad fałdą, zawartością za pomocą resolvera.
to dynamiczne podejście do ładowania strony może być wspomagane za pomocą szkieletów. Tak, że jeśli użytkownik przewija się natychmiast w dół, można dać użytkownikowi wskazanie, że zawartość jest ładowanie, a następnie poprawy UX.
Krok 1: Tworząc Resolver
musimy utworzyć resolver kątowy. Jednak nie ma polecenia Angular CLI, które generuje resolver. Dlatego będziemy musieli sami napisać dekorator (metadane resolvera).
na szczęście jest to tylko kilka linijek kodu, które tworzą boilerplate dla resolvera, i możemy pobrać dekorator iniekcji z istniejącego serwisu, Jeśli masz trudności z zapamiętaniem go.
Adnotacja klasy Resolver profilu za pomocą dekoratora do wstrzykiwania
najpierw zapewniamy dekorator do wstrzykiwania zprovidedIn: any
w konfiguracji.
@Injectable({ providedIn: 'any'})
nazwiemy nasz resolver dodając konwencję Resolver
. W tym przykładzie będziemy rozwiązywać dane profilu (dane użytkownika), więc nazwiemy je ProfileResolver
.
ponieważ jest to resolver, a Angular rozpoznaje funkcję resolverów, możemy zaimplementować klasęResolve
, która dostarczy podpis, który musimy zaimplementować w naszej funkcji resolve, aby pomyślnie rozwiązać dane.
@Injectable({providedIn: 'any'})
export class ProfileResolver implements Resolve<Profile> {
}
nasza funkcja resolve zwróci obserwowalne dane zgodne z interfejsemProfile
. Dlatego udostępnimy interfejsProfile
jako ogólny argument klasy resolver i funkcjęresolve()
. W ten sposób dostosowaliśmy się do wymagań kątowych dla resolvera.
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<T> | Promise<T> | T {
return;
}
implementacja resolve daje nam dwa parametry, jeśli są potrzebne,route
Istate
. Są one automatycznie wypełniane i dostępne z poziomu naszej funkcjiresolve()
.
następnie musimy faktycznie rozwiązać rzeczywiste dane z bazy danych, co będzie naszym następnym krokiem.
pobieranie danych z bazy danych
aby pobrać dane dla naszej funkcji resolve, będziemy musieli wstrzyknąć usługę dostarczającą dane; taką, która współdziała z bazą danych.
bierzemy od niego to, czego potrzebujemy, aby szybko rozwiązać problem, aby użytkownik szybko i skutecznie nawigował. Na potrzeby tej wersji demonstracyjnej nie będziemy się martwić o podstawową usługę, która zajmuje się bazą danych. Po prostu wstrzykniemy usługę za pomocą iniekcji zależności w argumencie konstruktora dla naszej klasy ProfileResolver
.
ponieważ nasze dane mają postać obserwowalnego strumienia danych z wieloma wartościami emitującymi asynchronicznie, wystarczytake(1)
używając operatora pipeabletake
importowanego zrxjs/operator
. W przeciwnym razie obserwowalny nigdy by się nie zakończył, a rozwiązujący nigdy by się nie rozwiązał.
potrzebujemy tylko jednej emisji/wartości i take
dopełnia obserwowalną dla nas wartość.
utworzenie resolvera jest tak proste, że wystarczy zwrócić obserwowalną w resolve()
funkcję, której angular będzie obsługiwał subskrypcję.
rozwiązujemy wszelkie błędy w pobieraniu danych poprzez przekierowanie do trasy nadrzędnej.
Bonus: Szybko uzupełniaj dynamiczne metadane SEO, zanim trasa zostanie załadowana
korzyści płynące z zapełnienia tagów <meta>
natychmiast mają oczywiste zalety. Im szybciej wypełniamy metadane SEO, tym szybciej nasze SEO poprawnie i dokładnie odzwierciedla zawartość strony.
oznacza to, że robotom, takim jak te obsługiwane przez wyszukiwarki, takie jak Google i Bing, łatwiej i szybciej można indeksować witrynę i pobierać zawartość.
to nie jest tak ważne na wstępnie renderowanych stronach lub tych, które są renderowane przez Angular Universal, ponieważ wszystkie renderowanie jest zakończone, zanim roboty otrzymają zawartość.
Jeśli jednak polegasz na (często Wątpliwej) zdolności robotów google do analizowania javascript dla Twojego SEO, lub masz rozwiązanie na żądanie, takie jak puppeteer, które wymaga pewności, że SEO będzie poprawne przed zwróceniem renderowanego drzewa DOM, wtedy pomocny powinien być resolver zawierający SEO. Pomaga więc, gdy gąsienica jest ograniczona czasowo.
również oddziela obawy od komponentu, dzięki czemu komponent nie musi radzić sobie z niczym związanym z SEO. Jeden z głównych powodów, dla których lubię resolvery.
Krok 2: wstrzyknięcie resolvera do modułu routingu
ProfileRoutingModule, w którym będziemy dostarczać nasz resolver, jest leniwym załadowanym modułem. Dlatego nasza ścieżka główna będzie pusta z parametrem token userSlug
, który będziemy musieli pobrać poprawne dane profilu.
aby dostarczyć nasz resolver, po prostu dostarczamy obiekt z nazwą naszych danych jako kluczem i konkretną resolver jako wartością, która będzie odpowiedzialna za rozwiązywanie tych danych.
możesz nazwać klucz jak chcesz, ale my po prostu nazwiemy go danymi.
to wszystko, co jest wymagane w module routingu, abyśmy mogli używać naszego resolvera.
następnie musimy pobrać i użyć naszych danych w komponencie.
Krok 3: Zainicjalizuj komponent z rozwiązanymi danymi
teraz, gdy rozwiązaliśmy nasze dane dotyczące aktywacji trasy, dane są dostępne za pośrednictwem usługiActivatedRoute
. Ponieważ mamy do czynienia z observables, w całej aplikacji utworzymy strumień, który wiąże się z właściwościądata
, która będzie naszą rozdzielczą danymi.
najpierw wstrzykniemyActivatedRoute
do konstruktora naszegoProfileComponent
. Następnie przypiszemythis.route.data
do obserwowalnegoprofile$
. Będziemy również chcieli przełączyć się na używanie obserwowalnego, gdy zaktualizowane dane dotrą z bazy danych, abyśmy mieli świeże dane podczas interakcji z aplikacją.
w tym celu użyjemystartWith
tak, że zaczniemy nasz strumień z wartością, która jest łatwo dostępna zthis.route.snapshot.data
. Następnie uzyskujemy dostęp dodata
właściwości jakthis.route.snapshot.data
startWith
wskazuje wartość na początek, jako pierwszą emisję naszego strumienia.
co natychmiast dostępne dane robią dla komponentu
natychmiast dostępne dane skracają czas ładowania poszczególnych części tej strony, który jest obserwowany przez użytkownika. Rezultatem nieużywania takiego resolvera jest to, że strona może się ładować w fragmentaryczny sposób, co nie jest przyjemne wizualnie.
w związku z tym musisz zwrócić uwagę na to, które elementy szablonów HTML są zależne od danych. Następnie należy napisać swój resolver, aby wspierać te elementy i ogólny wpływ na ładowanie strony UX.
istnieje wiele sposobów na to, że komponent może załadować fragmentowany
- jednym z nich jest, jeśli masz
ngIf
w wielu częściach szablonu HTML. - kolejny to
ngFor
.
najlepszą praktyką jest ograniczenie ilości indywidualnych
ngIf
's piszesz w celu ograniczenia ilości zmiany rozmiaru przeglądarki musi zrobić.
ładowanie strony przed pobraniem danych może spowodować ciągłe przeskakiwanie, opóźnienie i zmianę rozmiaru strony, powodując pogorszenie UX.
implementacja resolvera może być różnicą między użytkownikiem, który doświadcza 3-5 sekund skoku i zmiany rozmiaru w porównaniu z 0,5 sekundy, co często jest zbyt szybkie, aby uszkodzić ogólny UX.