Išsami React „useDeferredValue“ kabliuko analizė. Sužinokite, kaip išspręsti vartotojo sąsajos vėlavimą, suprasti konkurencingumą, palyginti su „useTransition“ ir kurti greitesnes programas pasaulinei auditorijai.
React „useDeferredValue“: Išsamus vadovas apie neblokuojantį vartotojo sąsajos našumą
Šiuolaikiniame žiniatinklio kūrimo pasaulyje vartotojo patirtis yra svarbiausia. Greita, reaguojanti sąsaja nebėra prabanga – tai lūkestis. Vartotojams visame pasaulyje, naudojantiems įvairiausius įrenginius ir esant skirtingoms tinklo sąlygoms, vėluojanti, stringanti vartotojo sąsaja gali nulemti, ar klientas sugrįš, ar bus prarastas. Būtent čia „React 18“ konkurentiškumo funkcijos, ypač useDeferredValue kabliukas, keičia žaidimo taisykles.
Jei kada nors kūrėte „React“ programą su paieškos laukeliu, kuris filtruoja didelį sąrašą, duomenų tinkleliu, kuris atsinaujina realiu laiku, ar sudėtingu prietaisų skydeliu, tikriausiai susidūrėte su bauginančiu vartotojo sąsajos užšalimu. Vartotojas rašo, ir sekundės dalį visa programa nustoja reaguoti. Taip nutinka, nes tradicinis atvaizdavimas „React“ yra blokuojantis. Būsenos atnaujinimas sukelia pervaizdavimą, ir niekas kitas negali įvykti, kol jis nebus baigtas.
Šis išsamus vadovas leis jums nuodugniai susipažinti su useDeferredValue kabliuku. Išnagrinėsime, kokią problemą jis sprendžia, kaip jis veikia „React“ naujojo konkurentiško variklio viduje ir kaip galite jį panaudoti kurdami neįtikėtinai reaguojančias programas, kurios atrodo greitos, net kai atlieka daug darbo. Aptarsime praktinius pavyzdžius, pažangius šablonus ir svarbiausias geriausias praktikas pasaulinei auditorijai.
Pagrindinės problemos supratimas: blokuojanti vartotojo sąsaja
Prieš įvertindami sprendimą, turime visiškai suprasti problemą. Ankstesnėse nei 18 „React“ versijose atvaizdavimas buvo sinchroniškas ir nepertraukiamas procesas. Įsivaizduokite vienos eismo juostos kelią: kai įvažiuoja automobilis (atvaizdavimas), joks kitas automobilis negali pravažiuoti, kol pirmasis nepasieks galo. Taip veikė „React“.
Panagrinėkime klasikinį scenarijų: produktų sąrašas su paieška. Vartotojas rašo tekstą paieškos laukelyje, o žemiau esantis tūkstančių elementų sąrašas filtruojamas pagal jo įvestį.
Tipiškas (ir lėtas) įgyvendinimas
Štai kaip kodas galėtų atrodyti „pre-React 18“ pasaulyje arba nenaudojant konkurentiškumo funkcijų:
Komponento struktūra:
Failas: SearchPage.js
import React, { useState } from 'react';
import ProductList from './ProductList';
import { generateProducts } from './data'; // funkcija, kuri sukuria didelį masyvą
const allProducts = generateProducts(20000); // Įsivaizduokime 20 000 produktų
function SearchPage() {
const [query, setQuery] = useState('');
const filteredProducts = allProducts.filter(product => {
return product.name.toLowerCase().includes(query.toLowerCase());
});
function handleChange(e) {
setQuery(e.target.value);
}
return (
Kodėl tai veikia lėtai?
Panagrinėkime vartotojo veiksmą:
- Vartotojas įveda raidę, tarkime, „a“.
- Suveikia onChange įvykis, iškviesdamas handleChange.
- Iškviečiama setQuery('a'). Tai suplanuoja SearchPage komponento pervaizdavimą.
- „React“ pradeda pervaizdavimą.
- Pervaizdavimo metu įvykdoma eilutė
const filteredProducts = allProducts.filter(...)
. Tai yra brangioji dalis. Filtruoti 20 000 elementų masyvą, net ir su paprastu „includes“ patikrinimu, užtrunka. - Kol vyksta šis filtravimas, naršyklės pagrindinė gija yra visiškai užimta. Ji negali apdoroti jokios naujos vartotojo įvesties, negali vizualiai atnaujinti įvesties lauko ir negali vykdyti jokio kito „JavaScript“ kodo. Vartotojo sąsaja yra blokuota.
- Kai filtravimas baigtas, „React“ pereina prie ProductList komponento atvaizdavimo, kas savaime gali būti sunki operacija, jei atvaizduojama tūkstančiai DOM mazgų.
- Galiausiai, po viso šio darbo, DOM yra atnaujinamas. Vartotojas mato, kad įvesties laukelyje atsiranda raidė „a“, o sąrašas atsinaujina.
Jei vartotojas rašo greitai – tarkime, „obuolys“ – visas šis blokavimo procesas vyksta su „o“, tada „ob“, „obu“, „obuo“, „obuol“ ir „obuolys“. Rezultatas – pastebimas vėlavimas, kai įvesties laukas stringa ir stengiasi neatsilikti nuo vartotojo rašymo. Tai prasta vartotojo patirtis, ypač mažiau galinguose įrenginiuose, kurie yra paplitę daugelyje pasaulio šalių.
Pristatome „React 18“ konkurencingumą
„React 18“ iš esmės keičia šią paradigmą, įvesdama konkurencingumą. Konkurencingumas nėra tas pats, kas lygiagretumas (daryti kelis dalykus tuo pačiu metu). Vietoj to, tai yra „React“ gebėjimas sustabdyti, atnaujinti arba nutraukti atvaizdavimą. Vienos eismo juostos kelias dabar turi lenkimo juostas ir eismo reguliuotoją.
Su konkurencingumu „React“ gali suskirstyti atnaujinimus į du tipus:
- Skubūs atnaujinimai: Tai dalykai, kurie turi atrodyti momentiniai, pavyzdžiui, rašymas įvesties laukelyje, mygtuko paspaudimas ar slankiklio vilkimas. Vartotojas tikisi nedelsiamo grįžtamojo ryšio.
- Perėjimo atnaujinimai: Tai atnaujinimai, kurie gali pakeisti vartotojo sąsają iš vieno vaizdo į kitą. Priimtina, jei jiems pasirodyti prireikia šiek tiek laiko. Sąrašo filtravimas ar naujo turinio įkėlimas yra klasikiniai pavyzdžiai.
Dabar „React“ gali pradėti neskubų „perėjimo“ atvaizdavimą, ir jei atsiranda skubesnis atnaujinimas (pavyzdžiui, kitas klavišo paspaudimas), jis gali sustabdyti ilgai trunkantį atvaizdavimą, pirmiausia apdoroti skubųjį, o tada tęsti savo darbą. Tai užtikrina, kad vartotojo sąsaja visada išliktų interaktyvi. useDeferredValue kabliukas yra pagrindinis įrankis šiai naujai galiai išnaudoti.
Kas yra `useDeferredValue`? Išsamus paaiškinimas
Iš esmės, useDeferredValue yra kabliukas, kuris leidžia jums pasakyti „React“, kad tam tikra reikšmė jūsų komponente nėra skubi. Jis priima reikšmę ir grąžina naują tos reikšmės kopiją, kuri „atsiliks“, jei vyksta skubūs atnaujinimai.
Sintaksė
Šį kabliuką naudoti yra neįtikėtinai paprasta:
import { useDeferredValue } from 'react';
const deferredValue = useDeferredValue(value);
Viskas. Jūs perduodate jam reikšmę, ir jis grąžina jums atidėtą tos reikšmės versiją.
Kaip tai veikia viduje
Išsklaidykime magiją. Kai naudojate useDeferredValue(query), štai ką daro „React“:
- Pradinis atvaizdavimas: Pirmojo atvaizdavimo metu deferredQuery bus toks pat kaip pradinis query.
- Įvyksta skubus atnaujinimas: Vartotojas įveda naują simbolį. query būsena pasikeičia iš „a“ į „ap“.
- Aukšto prioriteto atvaizdavimas: „React“ nedelsiant sukelia pervaizdavimą. Per šį pirmąjį, skubų pervaizdavimą, useDeferredValue žino, kad vyksta skubus atnaujinimas. Todėl jis vis dar grąžina ankstesnę reikšmę, „a“. Jūsų komponentas greitai pervaizduojamas, nes įvesties lauko reikšmė tampa „ap“ (iš būsenos), bet ta jūsų vartotojo sąsajos dalis, kuri priklauso nuo deferredQuery (lėtas sąrašas), vis dar naudoja seną reikšmę ir jos nereikia perskaičiuoti. Vartotojo sąsaja išlieka reaguojanti.
- Žemo prioriteto atvaizdavimas: Iškart po to, kai baigiasi skubus atvaizdavimas, „React“ fone pradeda antrą, neskubų pervaizdavimą. *Šiame* atvaizdavime useDeferredValue grąžina naują reikšmę, „ap“. Šis foninis atvaizdavimas sukelia brangią filtravimo operaciją.
- Pertraukiamumas: Štai svarbiausia dalis. Jei vartotojas įveda kitą raidę („app“), kol žemo prioriteto atvaizdavimas „ap“ vis dar vyksta, „React“ atmes tą foninį atvaizdavimą ir pradės iš naujo. Jis teikia pirmenybę naujam skubiam atnaujinimui („app“), o tada suplanuoja naują foninį atvaizdavimą su naujausia atidėta reikšme.
Tai užtikrina, kad brangus darbas visada atliekamas su naujausiais duomenimis ir niekada neblokuoja vartotojo nuo naujos įvesties. Tai galingas būdas sumažinti sunkių skaičiavimų prioritetą be sudėtingos rankinės „debouncing“ ar „throttling“ logikos.
Praktinis įgyvendinimas: lėtos paieškos pataisymas
Pakeiskime ankstesnį pavyzdį naudodami useDeferredValue, kad pamatytume jį veikiant.
Failas: SearchPage.js (Optimizuotas)
import React, { useState, useDeferredValue, useMemo } from 'react';
import ProductList from './ProductList';
import { generateProducts } from './data';
const allProducts = generateProducts(20000);
// Komponentas sąrašui rodyti, memoizuotas našumui
const MemoizedProductList = React.memo(ProductList);
function SearchPage() {
const [query, setQuery] = useState('');
// 1. Atidėkite užklausos reikšmę. Ši reikšmė atsiliks nuo „query“ būsenos.
const deferredQuery = useDeferredValue(query);
// 2. Brangus filtravimas dabar priklauso nuo deferredQuery.
// Mes taip pat apgaubiame tai useMemo tolimesniam optimizavimui.
const filteredProducts = useMemo(() => {
console.log('Filtruojama pagal:', deferredQuery);
return allProducts.filter(product => {
return product.name.toLowerCase().includes(deferredQuery.toLowerCase());
});
}, [deferredQuery]); // Perskaičiuojama tik pasikeitus deferredQuery
function handleChange(e) {
// Šis būsenos atnaujinimas yra skubus ir bus apdorotas nedelsiant
setQuery(e.target.value);
}
return (
Vartotojo patirties transformacija
Su šiuo paprastu pakeitimu vartotojo patirtis transformuojasi:
- Vartotojas rašo įvesties laukelyje, o tekstas pasirodo akimirksniu, be jokio vėlavimo. Taip yra todėl, kad įvesties value yra tiesiogiai susieta su query būsena, kuri yra skubus atnaujinimas.
- Žemiau esančiam produktų sąrašui gali prireikti sekundės dalies, kad pasivytų, tačiau jo atvaizdavimo procesas niekada neblokuoja įvesties lauko.
- Jei vartotojas rašo greitai, sąrašas gali atsinaujinti tik vieną kartą pačioje pabaigoje su galutiniu paieškos terminu, nes „React“ atmeta tarpinius, pasenusius foninius atvaizdavimus.
Dabar programa jaučiasi žymiai greitesnė ir profesionalesnė.
`useDeferredValue` ir `useTransition` palyginimas: koks skirtumas?
Tai yra vienas iš dažniausiai pasitaikančių painiavos taškų programuotojams, besimokantiems konkurentiško „React“. Tiek useDeferredValue, tiek useTransition yra naudojami atnaujinimams pažymėti kaip neskubiems, tačiau jie taikomi skirtingose situacijose.
Pagrindinis skirtumas yra: kur jūs turite kontrolę?
`useTransition`
useTransition naudojate, kai turite kontrolę ties kodu, kuris sukelia būsenos atnaujinimą. Jis suteikia jums funkciją, paprastai vadinamą startTransition, kuria galite apgaubti savo būsenos atnaujinimą.
const [isPending, startTransition] = useTransition();
function handleChange(e) {
const nextValue = e.target.value;
// Nedelsiant atnaujinkite skubiąją dalį
setInputValue(nextValue);
// Apgaubkite lėtą atnaujinimą startTransition
startTransition(() => {
setSearchQuery(nextValue);
});
}
- Kada naudoti: Kai patys nustatote būseną ir galite apgaubti setState iškvietimą.
- Pagrindinė savybė: Suteikia loginę isPending vėliavėlę. Tai nepaprastai naudinga rodant įkėlimo indikatorius ar kitą grįžtamąjį ryšį, kol vyksta perėjimas.
`useDeferredValue`
useDeferredValue naudojate, kai nekontroliuojate kodo, kuris atnaujina reikšmę. Tai dažnai nutinka, kai reikšmė ateina iš „props“, iš tėvinio komponento ar iš kito kabliuko, kurį teikia trečiosios šalies biblioteka.
function SlowList({ valueFromParent }) {
// Mes nekontroliuojame, kaip nustatoma valueFromParent.
// Mes tiesiog gauname ją ir norime atidėti atvaizdavimą remdamiesi ja.
const deferredValue = useDeferredValue(valueFromParent);
// ... naudokite deferredValue lėtajai komponento daliai atvaizduoti
}
- Kada naudoti: Kai turite tik galutinę reikšmę ir negalite apgaubti kodo, kuris ją nustatė.
- Pagrindinė savybė: Labiau „reaktyvus“ požiūris. Jis tiesiog reaguoja į besikeičiančią reikšmę, nesvarbu, iš kur ji atsirado. Jis neteikia integruotos isPending vėliavėlės, bet galite lengvai ją susikurti patys.
Palyginimo suvestinė
Savybė | `useTransition` | `useDeferredValue` |
---|---|---|
Ką apgaubia | Būsenos atnaujinimo funkciją (pvz., startTransition(() => setState(...)) ) |
Reikšmę (pvz., useDeferredValue(myValue) ) |
Kontrolės taškas | Kai kontroliuojate įvykio apdorojimo funkciją arba atnaujinimo paleidiklį. | Kai gaunate reikšmę (pvz., iš „props“) ir neturite kontrolės jos šaltiniui. |
Įkėlimo būsena | Suteikia integruotą `isPending` loginę reikšmę. | Nėra integruotos vėliavėlės, bet galima ją išvesti su `const isStale = originalValue !== deferredValue;`. |
Analogija | Jūs esate dispečeris, sprendžiantis, kuris traukinys (būsenos atnaujinimas) išvyks lėtuoju bėgiu. | Jūs esate stoties viršininkas, matantis traukiniu atvykstančią reikšmę ir nusprendžiantis ją trumpam palaikyti stotyje, prieš parodydamas pagrindinėje lentoje. |
Pažangūs naudojimo atvejai ir šablonai
Be paprasto sąrašų filtravimo, useDeferredValue atveria keletą galingų šablonų sudėtingoms vartotojo sąsajoms kurti.
1 šablonas: „pasenusios“ vartotojo sąsajos rodymas kaip grįžtamasis ryšys
Vartotojo sąsaja, kuri atsinaujina su nedideliu vėlavimu be jokio vizualinio grįžtamojo ryšio, vartotojui gali atrodyti klaidinga. Jie gali susimąstyti, ar jų įvestis buvo užregistruota. Puikus šablonas yra pateikti subtilų ženklą, kad duomenys atsinaujina.
Tai galite pasiekti palygindami pradinę reikšmę su atidėta reikšme. Jei jos skiriasi, tai reiškia, kad laukiamas foninis atvaizdavimas.
function SearchPage() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
// Ši loginė reikšmė parodo, ar sąrašas atsilieka nuo įvesties
const isStale = query !== deferredQuery;
const filteredProducts = useMemo(() => {
// ... brangus filtravimas naudojant deferredQuery
}, [deferredQuery]);
return (
Šiame pavyzdyje, kai tik vartotojas pradeda rašyti, isStale tampa „true“. Sąrašas šiek tiek išblunka, nurodydamas, kad netrukus atsinaujins. Kai atidėtas atvaizdavimas baigiasi, query ir deferredQuery vėl tampa lygūs, isStale tampa „false“, o sąrašas grįžta į pilną ryškumą su naujais duomenimis. Tai yra isPending vėliavėlės iš useTransition atitikmuo.
2 šablonas: atnaujinimų atidėjimas diagramose ir vizualizacijose
Įsivaizduokite sudėtingą duomenų vizualizaciją, pavyzdžiui, geografinį žemėlapį ar finansinę diagramą, kuri persipiešia remdamasi vartotojo valdomu slankikliu datos diapazonui. Slankiklio vilkimas gali būti labai trūkčiojantis, jei diagrama persipiešia su kiekvienu judesio pikseliu.
Atidėdami slankiklio reikšmę, galite užtikrinti, kad pati slankiklio rankenėlė išliks sklandi ir reaguojanti, o sunki diagrama grakščiai persipieš fone.
function ChartDashboard() {
const [year, setYear] = useState(2023);
const deferredYear = useDeferredValue(year);
// HeavyChart yra memoizuotas komponentas, atliekantis brangius skaičiavimus
// Jis persipieš tik tada, kai deferredYear reikšmė nusistovės.
const chartData = useMemo(() => computeChartData(deferredYear), [deferredYear]);
return (
Geriausios praktikos ir dažniausios klaidos
Nors useDeferredValue yra galingas, jį reikėtų naudoti apgalvotai. Štai keletas pagrindinių geriausių praktikų, kurių reikėtų laikytis:
- Pirmiausia profiliuokite, vėliau optimizuokite: Nenaudokite useDeferredValue visur. Naudokite „React DevTools Profiler“ norėdami nustatyti realias našumo problemas. Šis kabliukas skirtas konkrečiai situacijoms, kai pervaizdavimas yra išties lėtas ir sukelia prastą vartotojo patirtį.
- Visada memoizuokite atidėtą komponentą: Pagrindinė reikšmės atidėjimo nauda yra išvengti nereikalingo lėto komponento pervaizdavimo. Ši nauda visiškai realizuojama, kai lėtas komponentas yra apgaubtas React.memo. Tai užtikrina, kad jis persipieš tik tada, kai jo „props“ (įskaitant atidėtą reikšmę) iš tikrųjų pasikeičia, o ne per pradinį aukšto prioriteto atvaizdavimą, kai atidėta reikšmė vis dar yra sena.
- Pateikite vartotojui grįžtamąjį ryšį: Kaip aptarta „pasenusios vartotojo sąsajos“ šablone, niekada neleiskite vartotojo sąsajai atsinaujinti su vėlavimu be jokio vizualinio ženklo. Grįžtamojo ryšio trūkumas gali būti labiau painus nei pradinis vėlavimas.
- Neatidėkite pačios įvesties lauko reikšmės: Dažna klaida yra bandymas atidėti reikšmę, kuri valdo įvesties lauką. Įvesties lauko value savybė visada turi būti susieta su aukšto prioriteto būsena, kad būtų užtikrintas momentinis pojūtis. Jūs atidedate reikšmę, kuri perduodama lėtam komponentui.
- Supraskite `timeoutMs` parinktį (naudokite atsargiai): useDeferredValue priima neprivalomą antrą argumentą skirtą laikmačiui:
useDeferredValue(value, { timeoutMs: 500 })
. Tai nurodo „React“, kiek maksimaliai laiko jis turėtų atidėti reikšmę. Tai pažangi funkcija, kuri gali būti naudinga kai kuriais atvejais, tačiau paprastai geriau leisti „React“ valdyti laiką, nes jis optimizuotas pagal įrenginio galimybes.
Poveikis pasaulinei vartotojo patirčiai (UX)
Tokių įrankių kaip useDeferredValue pritaikymas yra ne tik techninis optimizavimas; tai įsipareigojimas geresnei, labiau įtraukiančiai vartotojo patirčiai pasaulinei auditorijai.
- Įrenginių lygybė: Programuotojai dažnai dirba su aukštos klasės kompiuteriais. Vartotojo sąsaja, kuri atrodo greita naujame nešiojamajame kompiuteryje, gali būti netinkama naudoti senesniame, mažos galios mobiliajame telefone, kuris yra pagrindinis interneto įrenginys didelei pasaulio gyventojų daliai. Neblokuojantis atvaizdavimas daro jūsų programą atsparesnę ir našesnę platesniame aparatinės įrangos spektre.
- Pagerintas prieinamumas: Užšąlanti vartotojo sąsaja gali būti ypač sudėtinga ekrano skaitytuvų ir kitų pagalbinių technologijų naudotojams. Pagrindinės gijos laisvumas užtikrina, kad šie įrankiai galėtų toliau sklandžiai veikti, suteikdami patikimesnę ir mažiau varginančią patirtį visiems vartotojams.
- Pagerintas suvokiamas našumas: Psichologija vaidina didžiulį vaidmenį vartotojo patirtyje. Sąsaja, kuri akimirksniu reaguoja į įvestį, net jei kai kurioms ekrano dalims atsinaujinti prireikia šiek tiek laiko, atrodo moderni, patikima ir gerai sukurta. Šis suvokiamas greitis stiprina vartotojų pasitikėjimą ir pasitenkinimą.
Išvada
React useDeferredValue kabliukas yra paradigmų kaita, kaip mes prieiname prie našumo optimizavimo. Užuot pasikliovę rankiniais ir dažnai sudėtingais metodais, tokiais kaip „debouncing“ ir „throttling“, dabar galime deklaratyviai nurodyti „React“, kurios mūsų vartotojo sąsajos dalys yra mažiau svarbios, leisdami jam daug protingiau ir vartotojui draugiškiau planuoti atvaizdavimo darbus.
Suprasdami pagrindinius konkurencingumo principus, žinodami, kada naudoti useDeferredValue, o kada useTransition, ir taikydami geriausias praktikas, tokias kaip memoizavimas ir vartotojo grįžtamasis ryšys, galite pašalinti vartotojo sąsajos strigimą ir kurti programas, kurios yra ne tik funkcionalios, bet ir malonios naudoti. Konkurencingoje pasaulinėje rinkoje greita, reaguojanti ir prieinama vartotojo patirtis yra pagrindinė savybė, o useDeferredValue yra vienas galingiausių įrankių jūsų arsenale jai pasiekti.