Implementacja ekranów szkieletowych w React
Opublikowany: 2022-03-10Spinnery i programy ładujące tradycyjnie były sposobem na poinformowanie użytkowników, że ładowanie treści zajmie trochę czasu. Chociaż to podejście jest świetne, szybko staje się przestarzałe we współczesnym rozwoju. Ekrany szkieletowe stają się idealnym zamiennikiem tradycyjnych ładowaczy, ponieważ skupiają się na postępach, a nie na czasie oczekiwania, zmniejszając w ten sposób frustrację związaną z ładowaniem.
W tym artykule nie będziemy omawiać podstaw składni CSS React ani JavaScript, więc nie musisz być ekspertem w żadnym z tych języków, aby kontynuować.
Eksperci od UI i UX uczą nas, że podczas gdy użytkownicy czekają na załadowanie treści na stronie, powinniśmy ich angażować.
Pomysł wykorzystania spinnerów do angażowania użytkowników przed załadowaniem treści jest świetny; jednak wynik może być mniej niż idealny, ponieważ większość użytkowników znudzi się, wpatrując się w sztuczny animowany spinner, jakby był zegarem. Rozwija się Łukasz Wróblewski.
Ekrany szkieletowe zapewniają lepsze wrażenia użytkownika, zmniejszając frustrację podczas ładowania. Koncentrując się na postępie zamiast na czasie oczekiwania, tworzy iluzję dla użytkowników, że informacje będą stopniowo wyświetlane na ekranie. Potwierdza to Bill Chung w swoich badaniach.
Co to jest ekran szkieletowy?
Ekran szkieletowy to wersja interfejsu użytkownika, która nie zawiera rzeczywistej zawartości; zamiast tego naśladuje układ strony, pokazując jej elementy w kształcie podobnym do rzeczywistej zawartości podczas ładowania i udostępniania (tj. gdy pozwala na to opóźnienie sieci).
Ekran szkieletowy jest zasadniczo szkieletem strony z polami zastępczymi na tekst i obrazy.
Co jest wyjątkowego w ekranie szkieletu?
Szkieletowy interfejs użytkownika przypomina rzeczywisty interfejs użytkownika strony, dzięki czemu użytkownicy zrozumieją, jak szybko załaduje się aplikacja internetowa lub mobilna, jeszcze zanim pojawi się zawartość. Oto kilka powodów, dla których warto rozważyć użycie ekranów szkieletowych w następnym projekcie:
- naśladowanie układu strony jest łatwiejsze dzięki ekranowi szkieletowemu,
- zawartość ładuje się progresywnie (nie wszystkie na raz).
Ekrany szkieletowe są również określane jako:
- elementy ducha,
- symbole zastępcze treści,
- ładowacze treści.
Blockchain.com, YouTube, Facebook, Medium i inne duże firmy technologiczne wyświetlają szkieletowe ekrany, podczas gdy ich zawartość ładuje się, aby poprawić UX.
Blockchain.com
Średni
Rodzaje ekranów szkieletowych
Istnieją różne rodzaje ekranów szkieletowych. Główne z nich to symbole zastępcze tekstu i symbole zastępcze obrazu (lub koloru).
Większość programistów woli używać tekstowych symboli zastępczych jako szkieletu interfejsu użytkownika na swoich stronach, ponieważ są one łatwe do zbudowania, a programista nie wymaga żadnych szczegółów dotyczących treści rzeczywistej zawartości; zamiast tego szkielet naśladuje interfejs użytkownika.
Symbole zastępcze kolorów są trudniejsze do zbudowania, ponieważ wymagają szczegółowych informacji o zawartości.
Niektóre popularne pakiety ułatwiają implementację ekranów szkieletowych w aplikacjach internetowych. Przyjrzyjmy się im obu:
- Reaguj zastępczy
- Szkielet ładowania reakcji
Przyjrzymy się zaletom i wadom każdego pakietu, zanim rozważymy, którego użyć w naszej aplikacji.
Reaguj zastępczy
Plusy
- Komponenty zastępcze są używane do tworzenia niestandardowego szkieletu interfejsu użytkownika.
- Obsługiwana jest animacja impulsowa (tj. efekt ruchu na elemencie).
- Jest dostarczany z interfejsem API opartym na komponentach.
Cons
- Komponenty szkieletu są obsługiwane oddzielnie, więc aktualizacja stylów komponentu prawdopodobnie wymaga również aktualizacji komponentu szkieletu.
- Krzywa uczenia się nie jest liniowa, ponieważ istnieje wiele elementów odpowiadających różnym potrzebom.
Poniżej znajduje się przykład komponentu szkieletu używającego pakietu react-placeholder
:
import { TextBlock, RectShape } from 'react-placeholder/lib/placeholders'; import ReactPlaceholder from 'react-placeholder'; const GhostPlaceholder = () => ( <div className='my-placeholder'> <RectShape color='gray' style={{width: 25, height: 70}} /> <TextBlock rows={6} color='blue'/> </div> ); <ReactPlaceholder ready={ready} customPlaceholder={<GhostPlaceholder />}> <MyComponent /> </ReactPlaceholder>
Po zaimportowaniu TextBlock
i RectShape
z React react-placeholder/lib/placeholder
oraz ReactPlaceholder
z React react-placeholder
, stworzyliśmy komponent funkcjonalny o nazwie GhostPlaceholder
. GhostPlaceholder
ma div, a wewnątrz div użyliśmy składnika RectShape, który opisuje wymiary prostokąta, przekazuje wartość dowolnego koloru i definiuje style prostokąta.
Następnie użyliśmy składnika TextBlock
do ustawienia wartości dla wierszy i koloru. Składnik TextBlock
definiuje liczbę wierszy i kolor tekstu.
Przekazujemy MyComponent
jako element potomny komponentu ReactPlaceholder
, który otrzymuje ready
i komponent GhostPlaceholder
jako wartości dla swoich właściwości ready
i customPlaceholder
.
MyComponent
zostanie załadowany, gdy wyświetlony zostanie interfejs użytkownika ekranu szkieletu.
Aby dowiedzieć się więcej, zapoznaj się z dokumentacją.
Szkielet ładowania reakcji
Plusy
- Jest oparty na API i ma jeden komponent z rekwizytami do wszystkich dostosowań.
- Może być używany jako osobny komponent szkieletu, a także bezpośrednio wewnątrz dowolnego komponentu, dzięki czemu jest elastyczny.
- Obsługuje motywy i animację Pulse.
Cons
- Jest łatwy do zaimplementowania w przypadku prostego szkieletowego interfejsu użytkownika, ale skomplikowany w przypadku bardziej złożonych szkieletów.
- Posiadanie oddzielnego komponentu szkieletu utrudni utrzymanie, gdy interfejs użytkownika i style się zmienią.
Poniżej znajduje się przykład React Loading Skeleton:
import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; const SkeletonComponent = () => ( <SkeletonTheme color="#202020" highlightColor="#444"> <section> <Skeleton height={50} width={50} /> </section> </SkeletonTheme> );
Zaimportowaliśmy Skeleton
i SkeletonTheme
z biblioteki react-loading-skeleton
, a następnie stworzyliśmy komponent funkcjonalny, który renderuje komponent SkeletonTheme
z właściwościami color
i hightlightColor
.
Składnik SkeletonTheme
służy do tworzenia motywów (na przykład dodawania efektów kolorystycznych do szkieletu interfejsu użytkownika).
Na koniec, wewnątrz sekcji, definiujemy komponent Skeleton
, z właściwościami wysokości i szerokości oraz ich odpowiednimi wartościami przekazanymi.
Tworzenie interfejsu użytkownika z ekranem przypominającym szkielet YouTube
Stwórzmy ekran szkieletu podobny do YouTube, używając React Loading Skeleton, aby pokazać, jak działa szkieletowy interfejs użytkownika.
Skonfiguruj React
Najłatwiejszym sposobem skonfigurowania Reacta jest użycie aplikacji Create React, która jest „oficjalnie obsługiwanym sposobem tworzenia jednostronicowych aplikacji React. Oferuje nowoczesną konfigurację kompilacji bez konfiguracji”.
Wykorzystamy go do załadowania aplikacji, którą będziemy budować. Na swoim terminalu uruchom poniższe polecenie:
npx create-react-app skeleton-screens && cd skeleton-screens
Po zakończeniu instalacji uruchom serwer React, uruchamiając npm start
:
Utwórz interfejs użytkownika YouTube bez ekranu szkieletu
Najpierw wprowadźmy fałszywe dane YouTube. Rzeczywiste punkty końcowe byłyby normalnie używane zamiast fikcyjnych danych, ale w tym samouczku użyjemy fikcyjnych danych.
Utwórz plik w swoim folderze src/
i nazwij go data.js
, dodaj do niego następujący kod.
const dummyData= [ { section: "Recommended", channel: "CNN", items: [ { id: "fDObf2AeAP4", image: "https://img.youtube.com/vi/fDObf2AeAP4/maxresdefault.jpg", title: "75 million Americans ordered to stay home", views: "1.9M views", published: "3 days agos" }, { id: "3AzIgAa0Cm8", image: "https://img.youtube.com/vi/3AzIgAa0Cm8/maxresdefault.jpg", title: "Gupta: The truth about using chloroquine to fight coronavirus pandemic", views: "128K views", published: "4 hours ago" }, { id: "92B37aXykYw", image: "https://img.youtube.com/vi/92B37aXykYw/maxresdefault.jpg", title: "Willie Jones STUNS Simon Cowell In Pitch Perfect Performance of 'Your Man'!", views: "2.47 million views", published: "1 month ago" }, { id: "J6rVaFzOEP8", image: "https://img.youtube.com/vi/J6rVaFzOEP8/maxresdefault.jpg", title: "Guide To Becoming A Self-Taught Software Developer", views: "104K views", published: "17 days ago" }, { id: "Wbk8ZrfU3EM", image: "https://img.youtube.com/vi/Wbk8ZrfU3EM/maxresdefault.jpg", title: "Tom Hanks and Rita Wilson test positive for coronavirus", views: "600k views", published: "1 week ago" }, { id: "ikHpFgKJax8", image: "https://img.youtube.com/vi/ikHpFgKJax8/maxresdefault.jpg", title: "Faces Of Africa- The Jerry Rawlings story", views: "2.3 million views", published: "2014" } ] }, { section: "Breaking News", channel: "CGTN America", items: [ { id: "tRLDPy1A8pI", image: "https://img.youtube.com/vi/tRLDPy1A8pI/maxresdefault.jpg", title: "Is Trump blaming China for COVID-19? You decide.", views: "876k views", published: "9 days ago" }, { id: "2ulH1R9hlG8", image: "https://img.youtube.com/vi/2ulH1R9hlG8/maxresdefault.jpg", title: "Journalist still goes to office during pandemic, see her daily routine", views: "873 views", published: "3 hours ago" }, { id: "TkfQ9MaIgU", image: "https://img.youtube.com/vi/_TkfQ9MaIgU/maxresdefault.jpg", title: "How are small businesses going to survive the economic downturn of the COVID-19 era?", views: "283 views", published: "4 day ago" } ] } ]; export default dummyData;
Aby odtworzyć format YouTube, utworzyliśmy fikcyjne dane zawierające szereg obiektów o właściwościach takich jak identyfikator, obraz, tytuł, liczba wyświetleń i data publikacji.
Następnie utwórzmy nasz interfejs użytkownika YouTube. Będziemy mieli trzy elementy:
Card | Zawiera szczegółowe informacje o miniaturze, tytule, liczbie wyświetleń, dacie publikacji i kanale filmu. |
CardList | Zwraca wszystkie karty z rzędu. |
App | Montuje nasz obiekt dummyData , ładuje szkielet interfejsu użytkownika na dwie sekundy i zwraca składnik CardList . |
Wewnątrz folderu src
utwórz folder i nazwij go components
. Wewnątrz folderu components
utwórz plik Card.js
, dodaj do niego następujący kod:
import React from "react"; const Card = ({ item, channel }) => { return ( <li className="card"> <a href={`https://www.youtube.com/watch?v=${item.id}`} target="_blank" rel="noopener noreferrer" className="card-link" > <img src={item.image} alt={item.title} className="card-image" /> <img src={item.image} alt={item.title} className="channel-image" /> <h4 className="card-title">{item.title}</h4> <p className="card-channel"> <i>{channel}</i> </p> <div className="card-metrics"> {item.views} • {item.published} </div> </a> </li> ); }; export default Card;
Stworzyliśmy komponent Card
. Wewnątrz zaimportowaliśmy React
z react
, a następnie zdekonstruowaliśmy item
i rekwizyty channel
, aby można było ich używać w komponencie Card
. Każdy element elementu Card
, który wyświetla jeden film, zawiera miniaturę, liczbę wyświetleń, datę publikacji i tytuł.
Składnik listy kart
Wewnątrz folderu components
utwórz plik CardList.js i dodaj do niego następujący kod:
import React from "react"; import Card from "./Card"; const CardList = ({ list }) => { return ( <ul className="list"> {list.items.map((item, index) => { return <Card key={index} item={item} channel={list.channel} />; })} </ul> ); }; export default CardList;
W tym komponencie zaimportowaliśmy utworzony przez nas komponent Card
. Karta akceptuje atrybuty item
i channel
, które otrzymujemy poprzez mapowanie poprzez list.items
. Następnie eksportujemy ten składnik jako CardList
, ponieważ będziemy go używać w naszym komponencie App
.
Uwaga : Tablica items , która jest odwzorowana w tym komponencie , jest tablicą obiektów w naszym dummyData
.
Komponent aplikacji
Wewnątrz pliku app.js w katalogu src/
usuń znajdujący się tam kod i dodaj do niego poniższy kod.
import React, { useState, useEffect } from "react"; import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]); const [loading, setLoading] = useState(false); useEffect(() => { setLoading(true); const timer = setTimeout(() => { setVideos(dummyData); setLoading(false); }, 5000); return () => clearTimeout(timer); }, []); return ( <div className="App"> { videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div> ); }; export default App;
W tym komponencie zaimportowaliśmy useState
i useEffect
razem z React
i innymi plikami, które stworzyliśmy i które będą potrzebne w komponencie App
.
Ponieważ nasze dane są fikcyjnymi danymi, musimy je wykreować, tak jak dane API, ładując zawartość po dwusekundowym oczekiwaniu, za pomocą metody JavaScript setTimeout
.
Następnie w komponencie App
tworzymy stan wideo i ustawiamy go na pustą tablicę za pomocą useState
.
Aby załadować nasze atrapy danych, użyjemy haka useEffect
. W naszym hooku tworzymy zmienną timer, która przechowuje funkcję setTimeout
()
. Wewnątrz funkcji ustawiamy nasz stan wideo na nasz obiekt dummyData
i upewniamy się, że dane zostaną załadowane po dwóch sekundach, a na koniec anulujemy licznik czasu podczas odmontowywania.
Na koniec mapujemy nasz stan wideo i zwracamy element section, który zawiera list-section
oraz składnik CardList
wraz z właściwościami listy.
Dodawanie CSS
Do tej pory używaliśmy wielu klas bez rzeczywistego CSS. Wewnątrz folderu src
usuń wszystko w App.css
i zastąp je następującym kodem;
.App { max-width: 960px; margin: 0 auto; font-size: 16px; } .list { display: flex; justify-content: space-between; flex-wrap: wrap; list-style: none; padding: 0; } .section-title { margin-top: 30px; } .card { width: calc(33% - 10px); margin: 20px 0; } .card-link { color: inherit; text-decoration: none; } .card-image { width: 100%; } .channel-image { border-radius: 100%; padding: 0, 10px, 0, 0; width: 40px; height: 40px; } .card-title { margin-top: 10px; margin-bottom: 0; } .card-channel { margin-top: 5px; margin-bottom: 5px; font-size: 14px; } /* Tablets */ @media (max-width: 1000px) { .App { max-width: 600px; } .card { width: calc(50% - 22px); } } /* Mobiles \*/ @media (max-width: 640px) { .App { max-width: 100%; padding: 0 15px; } .card { width: 100%; } }
Zobaczmy, jak wygląda nasz interfejs użytkownika YouTube bez ekranu szkieletowego. Widać, że po załadowaniu strony na dwie sekundy pojawia się biały ekran, a następnie dane ładują się natychmiast.
Korzystanie ze szkieletu ładowania React
W przeciwieństwie do innych bibliotek, w których skrupulatnie tworzysz szkielet ekranu, aby pasował do rozmiarów czcionek, wysokości linii i marginesów treści, komponent Skeleton
jest przeznaczony do użycia bezpośrednio w twoich komponentach, zamiast ładowanej treści.
Omówmy kilka powodów, dla których wybraliśmy React Loading Skeleton zamiast innych.
Tematy
React Loading Skeleton obsługuje motywy. W ten sposób możesz łatwo zmienić kolory wszystkich komponentów szkieletu za pomocą SkeletonTheme
i przekazać wartości do props
kolorów .
Poniżej przykład pokazujący, jak to działa:
import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; <SkeletonTheme color="grey" highlightColor="#444"> <p> <Skeleton height={250} width={300} count={1} /> </p> </SkeletonTheme> <SkeletonTheme color="#990" highlightColor="#550"> <p> <Skeleton height={250} width={300} count={1} /> </p> </SkeletonTheme>
Czas trwania
Oprócz rekwizytów height
, width
i color
możemy również określić rekwizyt duration
.
<Skeleton duration={2} />
Domyślny czas trwania to 1.2
. To określa, ile czasu zajmuje wykonanie jednego cyklu animacji szkieletu.
Aby dowiedzieć się więcej, zapoznaj się z dokumentacją.
Implementacja interfejsu użytkownika ekranu szkieletu
Teraz zainstalujemy react-loading-skeleton
. Uruchom następujące polecenie w swoim terminalu, aby zainstalować pakiet:
npm install react-loading-skeleton
Komponent szkieletu
Stwórzmy szkielet dla naszych danych wideo. Wewnątrz naszego folderu components
utwórz plik SkeletonCard.js
i dodaj następujący kod:
import React from "react"; import Skeleton from "react-loading-skeleton"; const SkeletonCard = () => { return ( <section> <h2 className="section-title"> <Skeleton height={30} width={300} /> </h2> <ul className="list"> {Array(9) .fill() .map((item, index) => ( <li className="card" key={index}> <Skeleton height={180} /> <h4 className="card-title"> <Skeleton circle={true} height={50} width={50} /> <Skeleton height={36} width={`80%`} /> </h4> <p className="card-channel"> <Skeleton width={`60%`} /> </p> <div className="card-metrics"> <Skeleton width={`90%`} /> </div> </li> ))} </ul> </section> ); }; export default SkeletonCard;
Stworzyliśmy listę nieuporządkowaną. Wewnątrz użyliśmy metody Array.fill()
. Ponieważ mamy dziewięć elementów fikcyjnych danych, użyliśmy metody Array.fill()
do zapętlenia się przez długość naszego obiektu items
i wypełniliśmy go bez wartości indeksu, dzięki czemu nasza tablica była pusta . Zobacz dokumentację Array.fill, aby dowiedzieć się, jak to działa.
Następnie zmapowaliśmy naszą pustą tablicę, aby zwrócić listę zawierającą właściwości szkieletu i określiliśmy wartość każdej z właściwości szkieletu.
Tutaj height
oznacza długość prostokąta szkieletu, a width
odnosi się do szerokości, podczas gdy circle
tworzy zaokrągloną część szkieletu interfejsu użytkownika.
React Loading Skeleton ma domyślną animację Pulse, co czyni go przydatnym. Możesz stworzyć animację Pulse, aby pasowała do twojego projektu, ale jeśli mnie o to poprosisz, pozostałbym przy domyślnej.
Wreszcie dostępny jest pełny kod źródłowy.
Mamy teraz w pełni funkcjonalny szkieletowy interfejs użytkownika. Nasz przykład pokazuje szkielet przez pięć sekund przed wyświetleniem zawartości.
Zobaczmy nasz dotychczasowy wynik:
Wniosek
Ekrany szkieletowe znacznie poprawiają wrażenia użytkownika, unikając frustracji związanej z całkowicie pustym ekranem i dając użytkownikowi wrażenie, jak treść będzie wyglądać przed załadowaniem.
Jeśli nie czujesz się komfortowo z żadnym z pakietów, które przejrzeliśmy, możesz utworzyć własny szkieletowy interfejs użytkownika, tworząc prostokąty i koła, które naśladują układ strony.
Podziel się swoją opinią i doświadczeniem w sekcji komentarzy poniżej. Chętnie zobaczę, co wymyślisz!
Repozytorium wspierające ten artykuł jest dostępne na Github.
Bibliografia
- „Wszystko, co musisz wiedzieć o ekranach szkieletowych”, Bill Chung, UX Collective
- „Szkielet ładuje strony za pomocą React”, Anthony Panagi, Octopus Wealth
- „Skeleton Screens z React And React Native”, Chris Dolphin, Alligator.io
- „Implementacja ładowania szkieletu w reakcji”, Adrian Bece, DEV