The playground

More information here

płynniejszy UX podczas ładowania strony z kątowymi Resolverami

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. […]

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ą

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 argumentuT, który będzie typem naszych rozwiązanych danych.

funkcja resolve zwracaObservablePromise 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ąfiltertakefirst . 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 taketake(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ę.

Klasa ProfileResolver

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.

nasz ProfileRoutingModule

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.datastartWith wskazuje wartość na początek, jako pierwszą emisję naszego strumienia.

nasz komponent profilu

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 maszngIf w wielu częściach szablonu HTML.
  • kolejny tongFor .

najlepszą praktyką jest ograniczenie ilości indywidualnychngIf '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.

to jest to! Mamy resolver z ulepszonym UX przy ładowaniu strony.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.