Avastage Reacti paralleelse andmete hankimise täiustatud tehnikaid, kasutades Suspense'i, parandades rakenduse jõudlust ja kasutajakogemust. Õppige strateegiaid mitme asünkroonse toimingu koordineerimiseks ja laadimise olekute tõhusaks haldamiseks.
React Suspense'i koordineerimine: paralleelse andmete hankimise valdamine
React Suspense on muutnud viisi, kuidas me käsitleme asünkroonseid toiminguid, eriti andmete hankimist. See võimaldab komponentidel renderdamise "peatada", kuni andmed laaditakse, pakkudes deklaratiivset viisi laadimise olekute haldamiseks. Lihtsalt üksikute andmete hankimiste Suspense'iga ümbristamine võib aga viia kaskaadiefektini, kus üks hankimine lõpeb enne järgmise algust, mõjutades negatiivselt jõudlust. See blogipostitus süveneb täiustatud strateegiatesse mitme andmete hankimise paralleelseks koordineerimiseks, kasutades Suspense'i, optimeerides teie rakenduse reageerimisvõimet ja parandades kasutajakogemust ülemaailmsele vaatajaskonnale.
Andmete hankimise kaskaadiprobleemi mõistmine
Kujutage ette stsenaariumi, kus peate kuvama kasutajaprofiili koos nende nime, avatari ja hiljutise tegevusega. Kui hangite iga andmeosa järjestikku, näeb kasutaja nime jaoks laadimise spinnerit, seejärel avatari jaoks teist ja lõpuks tegevuste voo jaoks ühte. See järjestikune laadimismuster loob kaskaadiefekti, viivitades täieliku profiili renderdamisega ja pettumust valmistades kasutajatele. Rahvusvaheliste kasutajate jaoks, kellel on erinev võrgukiirus, võib see viivitus olla veelgi suurem.
Kaaluge seda lihtsustatud koodilõiku:
function UserProfile() {
const name = useName(); // Hangib kasutajanime
const avatar = useAvatar(name); // Hangib avatari nime alusel
const activity = useActivity(name); // Hangib tegevuse nime alusel
return (
<div>
<h2>{name}</h2>
<img src={avatar} alt="Kasutaja Avatar" />
<ul>
{activity.map(item => <li key={item.id}>{item.text}</li>)}
</ul>
</div>
);
}
Selles näites sõltuvad useAvatar ja useActivity funktsiooni useName tulemusest. See loob selge kaskaadi - useAvatar ja useActivity ei saa andmete hankimist alustada enne, kui useName on lõpetatud. See on ebaefektiivne ja tavaline jõudluse kitsaskoht.
Strateegiad paralleelse andmete hankimiseks Suspense'iga
Andmete hankimise optimeerimise võti Suspense'iga on kõigi andmepäringute samaaegne algatamine. Siin on mitu strateegiat, mida saate kasutada:
1. Andmete eelnev laadimine funktsioonidega `React.preload` ja ressursid
Üks võimsamaid tehnikaid on andmete eelnev laadimine enne, kui komponent isegi renderdab. See hõlmab "ressursi" (andmete hankimise lubadust kapseldava objekti) loomist ja andmete eelnevat hankimist. `React.preload` aitab sellega. Selleks ajaks, kui komponent andmeid vajab, on need juba saadaval, kõrvaldades laadimise oleku peaaegu täielikult.
Kaaluge toote hankimise ressurssi:
const createProductResource = (productId) => {
let promise;
let product;
let error;
const suspender = new Promise((resolve, reject) => {
promise = fetch(`/api/products/${productId}`)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
product = data;
resolve();
})
.catch(e => {
error = e;
reject(e);
});
});
return {
read() {
if (error) {
throw error;
}
if (product) {
return product;
}
throw suspender;
},
};
};
// Kasutamine:
const productResource = createProductResource(123);
function ProductDetails() {
const product = productResource.read();
return (<div>{product.name}</div>);
}
Nüüd saate selle ressursi eelnevalt laadida enne komponendi ProductDetails renderdamist. Näiteks marsruudi üleminekute ajal või hõljumisel.
React.preload(productResource);
See tagab, et andmed on tõenäoliselt saadaval selleks ajaks, kui komponent ProductDetails neid vajab, minimeerides või kõrvaldades laadimise oleku.
2. `Promise.all` kasutamine samaaegseks andmete hankimiseks
Teine lihtne ja tõhus lähenemisviis on kasutada Promise.all kõigi andmete hankimiste samaaegseks algatamiseks ühe Suspense'i piiri sees. See toimib hästi, kui andmesõltuvused on eelnevalt teada.
Vaatame uuesti kasutajaprofiili näidet. Selle asemel, et andmeid järjestikku hankida, saame nime, avatari ja tegevuste voo samaaegselt hankida:
import { useState, useEffect, Suspense } from 'react';
async function fetchName() {
// Simuleeri API kõnet
await new Promise(resolve => setTimeout(resolve, 500));
return 'John Doe';
}
async function fetchAvatar(name) {
// Simuleeri API kõnet
await new Promise(resolve => setTimeout(resolve, 300));
return `https://example.com/avatars/${name.toLowerCase().replace(' ', '-')}.jpg`;
}
async function fetchActivity(name) {
// Simuleeri API kõnet
await new Promise(resolve => setTimeout(resolve, 800));
return [
{ id: 1, text: 'Postitas foto' },
{ id: 2, text: 'Uuendas profiili' },
];
}
function useSuspense(promise) {
const [result, setResult] = useState(null);
useEffect(() => {
let didCancel = false;
promise.then(
(data) => {
if (!didCancel) {
setResult({ status: 'success', value: data });
}
},
(error) => {
if (!didCancel) {
setResult({ status: 'error', value: error });
}
}
);
return () => {
didCancel = true;
};
}, [promise]);
if (result?.status === 'success') {
return result.value;
} else if (result?.status === 'error') {
throw result.value;
} else {
throw promise;
}
}
function Name() {
const name = useSuspense(fetchName());
return <h2>{name}</h2>;
}
function Avatar({ name }) {
const avatar = useSuspense(fetchAvatar(name));
return <img src={avatar} alt="Kasutaja Avatar" />;
}
function Activity({ name }) {
const activity = useSuspense(fetchActivity(name));
return (
<ul>
{activity.map(item => <li key={item.id}>{item.text}</li>)}
</ul>
);
}
function UserProfile() {
const name = useSuspense(fetchName());
return (
<div>
<Suspense fallback=<div>Laadin avatari...</div>>
<Avatar name={name} />
</Suspense>
<Suspense fallback=<div>Laadin tegevust...</div>>
<Activity name={name} />
</Suspense>
</div>
);
}
export default UserProfile;
Kui aga iga `Avatar` ja `Activity` sõltuvad ka `fetchName` funktsioonist, kuid renderdatakse eraldi suspense'i piirides, saate `fetchName` lubaduse tõsta üles vanemale ja edastada selle React Contexti kaudu.
import React, { createContext, useContext, useState, useEffect, Suspense } from 'react';
async function fetchName() {
// Simuleeri API kõnet
await new Promise(resolve => setTimeout(resolve, 500));
return 'John Doe';
}
async function fetchAvatar(name) {
// Simuleeri API kõnet
await new Promise(resolve => setTimeout(resolve, 300));
return `https://example.com/avatars/${name.toLowerCase().replace(' ', '-')}.jpg`;
}
async function fetchActivity(name) {
// Simuleeri API kõnet
await new Promise(resolve => setTimeout(resolve, 800));
return [
{ id: 1, text: 'Postitas foto' },
{ id: 2, text: 'Uuendas profiili' },
];
}
function useSuspense(promise) {
const [result, setResult] = useState(null);
useEffect(() => {
let didCancel = false;
promise.then(
(data) => {
if (!didCancel) {
setResult({ status: 'success', value: data });
}
},
(error) => {
if (!didCancel) {
setResult({ status: 'error', value: error });
}
}
);
return () => {
didCancel = true;
};
}, [promise]);
if (result?.status === 'success') {
return result.value;
} else if (result?.status === 'error') {
throw result.value;
} else {
throw promise;
}
}
const NamePromiseContext = createContext(null);
function Avatar() {
const namePromise = useContext(NamePromiseContext);
const name = useSuspense(namePromise);
const avatar = useSuspense(fetchAvatar(name));
return <img src={avatar} alt="Kasutaja Avatar" />;
}
function Activity() {
const namePromise = useContext(NamePromiseContext);
const name = useSuspense(namePromise);
const activity = useSuspense(fetchActivity(name));
return (
<ul>
{activity.map(item => <li key={item.id}>{item.text}</li>)}
</ul>
);
}
function UserProfile() {
const namePromise = fetchName();
return (
<NamePromiseContext.Provider value={namePromise}>
<Suspense fallback=<div>Laadin avatari...</div>>
<Avatar />
</Suspense>
<Suspense fallback=<div>Laadin tegevust...</div>>
<Activity />
</Suspense>
</NamePromiseContext.Provider>
);
}
export default UserProfile;
3. Kohandatud hook'i kasutamine paralleelsete hankimiste haldamiseks
Keerukamate stsenaariumide korral, kus on potentsiaalselt tingimuslikud andmesõltuvused, saate luua kohandatud hook'i paralleelse andmete hankimise haldamiseks ja tagastada ressursi, mida Suspense saab kasutada.
import { useState, useEffect, useRef } from 'react';
function useParallelData(fetchFunctions) {
const [resource, setResource] = useState(null);
const mounted = useRef(true);
useEffect(() => {
mounted.current = true;
const promises = fetchFunctions.map(fn => fn());
const suspender = Promise.all(promises).then(
(results) => {
if (mounted.current) {
setResource({ status: 'success', value: results });
}
},
(error) => {
if (mounted.current) {
setResource({ status: 'error', value: error });
}
}
);
setResource({
status: 'pending',
value: suspender,
});
return () => {
mounted.current = false;
};
}, [fetchFunctions]);
const read = () => {
if (!resource) {
throw new Error('Ressurss pole veel initsialiseeritud');
}
if (resource.status === 'pending') {
throw resource.value;
}
if (resource.status === 'error') {
throw resource.value;
}
return resource.value;
};
return { read };
}
// Näide kasutamisest:
async function fetchUserData(userId) {
// Simuleeri API kõnet
await new Promise(resolve => setTimeout(resolve, 300));
return { id: userId, name: 'Kasutaja ' + userId };
}
async function fetchUserPosts(userId) {
// Simuleeri API kõnet
await new Promise(resolve => setTimeout(resolve, 500));
return [{ id: 1, title: 'Postitus 1' }, { id: 2, title: 'Postitus 2' }];
}
function UserProfile({ userId }) {
const { read } = useParallelData([
() => fetchUserData(userId),
() => fetchUserPosts(userId),
]);
const [userData, userPosts] = read();
return (
<div>
<h2>{userData.name}</h2>
<ul>
{userPosts.map(post => <li key={post.id}>{post.title}</li>)}
</ul>
</div>
);
}
function App() {
return (
<Suspense fallback=<div>Laadin kasutaja andmeid...</div>>
<UserProfile userId={123} />
</Suspense>
);
}
export default App;
See lähenemisviis kapseldab lubaduste ja laadimise olekute haldamise keerukuse hook'i sisse, muutes komponendi koodi puhtamaks ja rohkem keskendunuks andmete renderdamisele.
4. Selektiivne hüdratsioon voogesituse serveripoolse renderdamisega
Serveripoolselt renderdatud rakenduste puhul tutvustab React 18 selektiivset hüdratsiooni voogesituse serveripoolse renderdamisega. See võimaldab teil saata HTML-i kliendile tükkidena, kui see serveris kättesaadavaks saab. Saate aeglaselt laadivad komponendid ümbritseda <Suspense> piiridega, võimaldades ülejäänud lehel interaktiivseks muutuda, samal ajal kui aeglased komponendid serveris laaditakse. See parandab dramaatiliselt tajutavat jõudlust, eriti kasutajate jaoks, kellel on aeglane võrguühendus või seadmed.
Kaaluge stsenaariumi, kus uudiste veebisait peab kuvama artikleid maailma erinevatest piirkondadest (nt Aasia, Euroopa, Ameerika). Mõned andmeallikad võivad olla aeglasemad kui teised. Selektiivne hüdratsioon võimaldab kuvada artikleid kiirematest piirkondadest esimesena, samal ajal kui aeglasematest piirkondadest pärit artiklid laaditakse, vältides kogu lehe blokeerimist.
Vigade ja laadimise olekute käsitlemine
Kuigi Suspense lihtsustab laadimise oleku haldamist, on vigade käsitlemine endiselt ülioluline. Vigarajad (kasutades componentDidCatch elutsükli meetodit või useErrorBoundary hook'i sellistest teekidest nagu `react-error-boundary`) võimaldavad teil graatsiliselt käsitleda vigu, mis ilmnevad andmete hankimise või renderdamise ajal. Need vigarajad tuleks paigutada strateegiliselt, et püüda vigu konkreetsete Suspense'i piiride sees, vältides kogu rakenduse krahhi.
import React, { Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
function MyComponent() {
// ... hangib andmeid, mis võivad põhjustada vigu
}
function App() {
return (
<ErrorBoundary fallback={<div>Midagi läks valesti!</div>}>
<Suspense fallback={<div>Laadimine...</div>}>
<MyComponent />
</Suspense>
</ErrorBoundary>
);
}
Ärge unustage pakkuda informatiivset ja kasutajasõbralikku varu-UI nii laadimise kui ka veaolukordade jaoks. See on eriti oluline rahvusvaheliste kasutajate jaoks, kes võivad kokku puutuda aeglasema võrgukiirusega või piirkondlike teenusekatkestustega.
Parimad tavad andmete hankimise optimeerimiseks Suspense'iga
- Tuvastage ja seadke prioriteediks kriitilised andmed: tehke kindlaks, millised andmed on rakenduse esmaseks renderdamiseks hädavajalikud, ja seadke nende andmete hankimine esikohale.
- Laadige andmeid eelnevalt, kui võimalik: kasutage `React.preload` ja ressursse, et laadida andmeid eelnevalt enne, kui komponendid neid vajavad, minimeerides laadimise olekuid.
- Hankige andmeid samaaegselt: kasutage `Promise.all` või kohandatud hook'e, et algatada mitu andmete hankimist paralleelselt.
- Optimeerige API lõpp-punkte: veenduge, et teie API lõpp-punktid on jõudluse jaoks optimeeritud, minimeerides latentsust ja koormuse suurust. Kaaluge selliste tehnikate nagu GraphQL kasutamist, et hankida ainult vajalikud andmed.
- Rakendage vahemällu salvestamist: salvestage sageli kasutatavad andmed vahemällu, et vähendada API päringute arvu. Kaaluge selliste teekide nagu `swr` või `react-query` kasutamist tugevate vahemällu salvestamise võimaluste jaoks.
- Kasutage koodi tükeldamist: jagage oma rakendus väiksemateks tükkideks, et vähendada esialgset laadimisaega. Kombineerige koodi tükeldamine Suspense'iga, et progressiivselt laadida ja renderdada rakenduse erinevaid osi.
- Jälgige jõudlust: jälgige regulaarselt oma rakenduse jõudlust, kasutades selliseid tööriistu nagu Lighthouse või WebPageTest, et tuvastada ja kõrvaldada jõudluse kitsaskohad.
- Käsitlege vigu graatsiliselt: rakendage vigarajasid, et püüda vigu andmete hankimise ja renderdamise ajal, pakkudes kasutajatele informatiivseid veateateid.
- Kaaluge serveripoolset renderdamist (SSR): SEO ja jõudluse kaalutlustel kaaluge SSR-i kasutamist voogesituse ja selektiivse hüdratsiooniga, et pakkuda kiiremat esialgset kogemust.
Järeldus
React Suspense, kui seda kombineerida paralleelse andmete hankimise strateegiatega, pakub võimsa tööriistakomplekti reageerivate ja suure jõudlusega veebirakenduste loomiseks. Mõistes kaskaadiprobleemi ja rakendades selliseid tehnikaid nagu eelnev laadimine, samaaegne hankimine funktsiooniga Promise.all ja kohandatud hook'id, saate oluliselt parandada kasutajakogemust. Ärge unustage vigu graatsiliselt käsitleda ja jõudlust jälgida, et tagada oma rakenduse optimeerimine kasutajatele kogu maailmas. Kuna React areneb edasi, suurendab selliste uute funktsioonide uurimine nagu selektiivne hüdratsioon voogesituse serveripoolse renderdamisega veelgi teie võimet pakkuda erakordseid kasutajakogemusi, olenemata asukohast või võrgutingimustest. Neid tehnikaid omaks võttes saate luua rakendusi, mis pole mitte ainult funktsionaalsed, vaid ka rõõm kasutada teie ülemaailmsele vaatajaskonnale.
Selle blogipostituse eesmärk on anda põhjalik ülevaade paralleelse andmete hankimise strateegiatest React Suspense'iga. Loodame, et see oli teile informatiivne ja kasulik. Soovitame teil nende tehnikatega oma projektides katsetada ja oma tulemusi kogukonnaga jagada.