Implementarea ecranelor schelet în React
Publicat: 2022-03-10Spinnerele și încărcătoarele au fost în mod tradițional modalitatea de a le spune utilizatorilor că conținutul va dura ceva timp pentru a se încărca. Deși această abordare este grozavă, devine rapid învechită în dezvoltarea modernă. Ecranele de tip schelet devin înlocuitorul perfect pentru încărcătoarele tradiționale, deoarece se concentrează mai degrabă pe progres decât pe timpii de așteptare, reducând astfel frustrarea în timpul încărcării.
În acest articol, nu vom trece prin elementele de bază ale CSS React sau sintaxei JavaScript, așa că nu trebuie să fii un expert în niciuna dintre aceste limbi pentru a urma.

Experții UI și UX ne învață că, în timp ce utilizatorii așteaptă ca conținutul să se încarce pe o pagină, ar trebui să-i menținem implicați.
Ideea din spatele utilizării filatoarelor pentru a implica utilizatorii înainte de încărcarea conținutului este grozavă; cu toate acestea, rezultatul poate fi mai puțin decât ideal, deoarece majoritatea utilizatorilor se vor plictisi privind la un rotor animat fals ca și cum ar fi un ceas. Luke Wroblewski detaliază acest lucru.
Ecranele schelet oferă o experiență mai bună pentru utilizator, reducând frustrarea în timpul încărcării. Concentrându-se pe progres în loc de timpii de așteptare, se creează iluzia utilizatorilor că informațiile vor fi afișate progresiv pe ecran. Bill Chung în cercetările sale confirmă acest lucru.
Ce este un ecran schelet?
Un ecran schelet este o versiune a interfeței de utilizare care nu conține conținut real; în schimb, imită aspectul paginii, arătând elementele acesteia într-o formă similară cu conținutul real, pe măsură ce se încarcă și devine disponibil (adică atunci când latența rețelei permite).
Un ecran schelet este în esență un cadru fir al paginii, cu casete de substituent pentru text și imagini.
Ce este unic la un ecran schelet?
O interfață de utilizare schelet seamănă cu interfața de utilizare reală a paginii, astfel încât utilizatorii vor înțelege cât de repede se va încărca aplicația web sau mobilă chiar înainte ca conținutul să apară. Iată câteva motive pentru care ați putea dori să luați în considerare utilizarea ecranelor schelet în următorul dvs. proiect:
- imitarea aspectului unei pagini este mai ușoară cu un ecran schelet,
- continutul se incarca progresiv (nu tot odata).
Ecranele scheletului sunt denumite și ca:
- elemente fantomă,
- substituenți de conținut,
- încărcătoare de conținut.
Blockchain.com, YouTube, Facebook, Medium și alte companii mari de tehnologie afișează ecrane schelet în timp ce conținutul lor se încarcă pentru a stimula UX.
Blockchain.com

Mediu


Tipuri de ecrane scheletice
Există diferite tipuri de ecrane scheletice. Cele mai importante sunt substituenții de text și substituenții de imagine (sau de culoare).
Majoritatea dezvoltatorilor preferă să folosească substituenți de text ca interfață de utilizare schelet în paginile lor, deoarece sunt ușor de construit, iar dezvoltatorul nu necesită detalii despre substanța conținutului real; în schimb scheletul imită UI.
Substituenții de culoare sunt mai greu de construit, deoarece necesită detalii despre conținut.
Unele pachete populare facilitează implementarea ecranelor schelet în aplicațiile web. Să aruncăm o privire mai atentă la ambele:
- Substituent de reacție
- Reacționează încărcând scheletul
Ne vom uita la avantajele și dezavantajele fiecărui pachet, înainte de a ne gândi pe care să folosim pentru aplicația noastră.
Substituent de reacție
Pro
- Componentele substituenților sunt folosite pentru a crea o interfață de utilizare cu schelet personalizat.
- Animația cu puls (adică efectul de mișcare asupra unui element) este acceptată.
- Vine cu un API bazat pe componente.
Contra
- Componentele scheletului sunt menținute separat, astfel încât actualizarea stilurilor unei componente necesită, eventual, actualizarea componentei scheletului.
- Curba de învățare nu este liniară deoarece există mai multe componente pentru nevoi diferite.
Următorul este un exemplu de componentă schelet folosind pachetul 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>
TextBlock
și RectShape
din react-placeholder/lib/placeholder
și ReactPlaceholder
din react-placeholder
, am creat o componentă funcțională numită GhostPlaceholder
. GhostPlaceholder
are un div, iar în interiorul div-ului am folosit componenta RectShape, care descrie dimensiunile unui dreptunghi, transmite valoarea oricărei culori și definește stilurile dreptunghiului.
Apoi, am folosit componenta TextBlock
pentru a seta valorile pentru rânduri și culoare. Componenta TextBlock
definește numărul de rânduri și culoarea textului.
Trecem MyComponent
ca un copil al componentei ReactPlaceholder
, care primește ready
și componenta GhostPlaceholder
ca valori pentru elementele sale de recuzită ready
și customPlaceholder
.
MyComponent
se va încărca când este afișată interfața de utilizare a ecranului schelet.
Pentru a afla mai multe, verificați documentația.
Reacționează încărcând scheletul
Pro
- Este bazat pe API și are o componentă cu elemente de recuzită pentru toate personalizările.
- Poate fi folosit ca o componentă schelet separată și, de asemenea, în interiorul oricărei componente direct, deci este flexibil.
- Acceptă tematică și animație Pulse.
Contra
- Este ușor de implementat pentru o interfață de utilizare schelet simplă, dar complicat pentru schelete mai complexe.
- Având o componentă schelet separată, va fi mai dificil de întreținut atunci când interfața de utilizare și stilurile se schimbă.
Următorul este un exemplu de 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> );
Am importat Skeleton
și SkeletonTheme
din biblioteca react-loading-skeleton
, apoi am creat o componentă funcțională care redă componenta SkeletonTheme
, cu color
și hightlightColor
ca proprietăți.
Componenta SkeletonTheme
este utilizată pentru tematică (de exemplu, adăugarea de efecte de culoare la interfața de utilizare a scheletului).
În cele din urmă, în interiorul secțiunii, definim componenta Skeleton
, cu proprietățile de înălțime și lățime și valorile lor corespunzătoare transmise.
Crearea unei interfețe de utilizare a ecranului schelet asemănătoare YouTube
Să creăm un ecran schelet asemănător YouTube, folosind React Loading Skeleton, pentru a arăta cum funcționează o interfață de utilizare schelet.
Configurați React
Cel mai simplu mod de a configura React este să utilizați aplicația Create React, care este „o modalitate acceptată oficial de a crea aplicații React cu o singură pagină. Oferă o configurație modernă, fără configurație.”
Îl vom folosi pentru a porni aplicația pe care o vom construi. De pe terminalul dvs., executați comanda de mai jos:
npx create-react-app skeleton-screens && cd skeleton-screens
Odată ce instalarea s-a încheiat, porniți serverul React rulând npm start
:

Creați interfața de utilizare YouTube fără un ecran schelet
Mai întâi, haideți să introducem date false YouTube. În mod normal, punctele finale reale ar fi folosite în loc de date fictive, dar în acest tutorial vom folosi date fictive.
Creați un fișier în folderul dvs. src/
și denumiți-l data.js
, adăugați următorul cod la el.
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;
Pentru a reproduce formatul YouTube, am creat date fictive care au o serie de obiecte, cu proprietăți precum ID, imagine, titlu, numărul de vizualizări și data publicării.

Apoi, să creăm interfața noastră de utilizare YouTube. Vom avea trei componente:
Card | Conține detaliile miniaturii, titlului, numărul de vizionări, data publicării și canalul videoclipului. |
CardList | Returnează toate cărțile la rând. |
App | Montează obiectul nostru dummyData , încarcă interfața de utilizare schelet timp de două secunde și returnează componenta CardList . |
În interiorul folderului src
, creați un folder și denumiți-l components
. În folderul components
, creați un fișier Card.js
, adăugați următorul cod la acesta:
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;
Am creat o componentă Card
. În interiorul acestuia, am importat React
din react
și am deconstruit item
și elementele de recuzită ale channel
astfel încât acestea să poată fi folosite în componenta Card
. Fiecare componentă a articolului Card
care afișează un videoclip va afișa miniatura, numărul de vizualizări, data publicării și titlul.
Componenta CardList
În interiorul folderului components
, creați un fișier CardList.js și adăugați următorul cod la acesta:
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;
În această componentă, am importat componenta Card
pe care am creat-o. Cardul acceptă item
și elementele de recuzită ale channel
, pe care le obținem prin maparea prin list.items
. Exportăm apoi această componentă ca CardList
, deoarece o vom folosi în componenta noastră App
.
Notă : Matricea de articole care este mapată în această componentă este matricea de obiecte din dummyData
.
Componenta aplicației
În interiorul fișierului app.js din directorul src/
, ștergeți codul care se află acolo și adăugați următoarele.
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;
În această componentă, am importat cârligele useState
și useEffect
alături de React
și alte fișiere pe care le-am creat și care vor fi necesare în componenta App
.
Deoarece datele noastre sunt date fictive, trebuie să le facem joc ca și datele API, încărcând conținutul după un timeout de două secunde, folosind metoda JavaScript setTimeout
.
Apoi, în componenta App
, creăm o stare video și setăm starea la o matrice goală folosind useState
.
Pentru a încărca datele noastre fictive, vom folosi cârligul useEffect
. În cârligul nostru, creăm un temporizator variabil care deține funcția setTimeout
()
. În cadrul funcției, ne setăm starea video la obiectul nostru dummyData
și ne asigurăm că datele se încarcă după două secunde și, în sfârșit, anulăm temporizatorul în timpul demontării.
În cele din urmă, mapăm starea noastră video și returnăm elementul de secțiune care conține secțiunea list-section
și componenta CardList
cu elementele sale de recuzită.
Adăugarea CSS
Până acum, am folosit o mulțime de clase fără CSS real. În interiorul folderului src
, ștergeți totul din App.css
și înlocuiți-l cu următorul cod;
.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%; } }
Să vedem cum arată interfața noastră de utilizare YouTube fără ecranul schelet. Puteți vedea că atunci când pagina se încarcă, apare un ecran alb timp de două secunde, apoi datele se încarcă prompt.

Folosind React Loading Skeleton
Spre deosebire de alte biblioteci în care ați crea cu meticulozitate un ecran schelet pentru a se potrivi cu dimensiunile fontului, înălțimile liniilor și marginile conținutului dvs., componenta Skeleton
este concepută pentru a fi utilizată direct în componentele dvs., în locul conținutului care se încarcă.
Să trecem peste câteva motive pentru care am ales React Loading Skeleton în locul altora.
Tematică
React Loading Skeleton acceptă tematica. Astfel, puteți schimba cu ușurință culorile tuturor componentelor scheletului utilizând SkeletonTheme
și transmiteți valori la elementele de props
de culoare.
Mai jos este un exemplu care arată cum funcționează:
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>

Durată
Pe lângă elementele de recuzită height
, width
și color
, putem specifica și o recuzită pentru duration
.
<Skeleton duration={2} />
Durata este implicită la 1.2
. Aceasta determină cât timp durează pentru a face un ciclu al animației scheletului.
Pentru a afla mai multe, consultați documentația.
Implementarea Skeleton Screen UI
Acum, vom instala react-loading-skeleton
. Rulați următoarea comandă în terminalul dvs. pentru a instala pachetul:
npm install react-loading-skeleton
Componenta scheletului
Să creăm o componentă schelet pentru datele noastre video. În dosarul nostru de components
, creați un fișier SkeletonCard.js
și adăugați următorul cod:
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;
Am creat o listă neordonată. În interiorul acestuia, am folosit metoda Array.fill()
. Deoarece avem nouă elemente de date fictive, am folosit metoda Array.fill()
pentru a parcurge lungimea obiectului nostru items
și l-am umplut fără valoare de index, făcând astfel matricea noastră goală . Consultați documentația Array.fill pentru a afla cum funcționează.
Apoi, am mapat prin matricea noastră goală pentru a returna o listă care conține proprietățile scheletului și am specificat valoarea fiecăreia dintre proprietățile scheletului.
Aici, height
conotă lungimea unui dreptunghi schelet, iar width
se referă la lățime, în timp ce circle
creează partea rotunjită a interfeței de utilizare a scheletului.
React Loading Skeleton vine cu o animație implicită Pulse, ceea ce o face la îndemână. Ai putea crea o animație Pulse pentru a se potrivi proiectului tău, dar dacă mă întrebi, aș rămâne cu cea implicită.
În cele din urmă, codul sursă complet este disponibil.
Acum avem o interfață de utilizare a ecranului schelet complet funcțională. Exemplul nostru arată scheletul timp de cinci secunde înainte de a afișa conținutul.
Să vedem rezultatul nostru de până acum:

Concluzie
Ecranele schelet îmbunătățesc enorm experiența utilizatorului, evitând frustrarea de a se confrunta cu un ecran complet gol și oferind utilizatorului o impresie despre cum va arăta conținutul înainte de a se încarca.
Dacă nu vă simțiți confortabil cu niciunul dintre pachetele pe care le-am analizat, vă puteți crea propria interfață de utilizare schelet făcând dreptunghiuri și cercuri care imită aspectul paginii.
Vă rugăm să împărtășiți feedbackul și experiența dvs. în secțiunea de comentarii de mai jos. Mi-ar plăcea să văd cu ce ai venit!
Repo de sprijin pentru acest articol este disponibil pe Github.
Referințe
- „Tot ce trebuie să știți despre ecranele schelet”, Bill Chung, UX Collective
- „Skeleton Loading Pages With React”, Anthony Panagi, Octopus Wealth
- „Scheleton Screens With React And React Native”, Chris Dolphin, Alligator.io
- „Implementarea Skeleton Loading In React”, Adrian Bece, DEV