We hebben allemaal de frustratie ervaren van lang wachten op het verschijnen van een pagina, om erachter te komen dat de pagina niet reageert. U ziet loadingspinner overal, maar er lijkt geen vooruitgang te zijn. Laat me een helder beeld voor u opdoen:

Dit gebeurt meestal omdat de website probeert alle noodzakelijke data te ophalen zodra u op de pagina arriveert. Het kan zijn dat een API-verzoek wordt verwerkt, of dat meerdere API’s gegevens sequentieel ophalen, waardoor de paginalading vertraagd wordt.

Het resultaat? Een slechte gebruikerservaring. U zou wellicht denken: “Hoe kan zo’n grote onderneming de gebruikerservaring niet prioriteren? Dit is teleurstellend.” Daardoor verlaten gebruikers vaak de site, wat belangrijke meetgegevens en mogelijk de omzet kan beïnvloeden.

Maar wat als u de data voor deze zware pagina’s alvorens de pagina wordt bezocht kan ophalen, zodat de gebruiker direct kan interacteren met de pagina?

Daar komt de conceptuele voorbereiding aan, en dat is exact wat we in dit blogartikel diepgaan zullen ingaan. Zonder verdere verwachtingen, laten we beginnen!

Inhoudsopgave

Prefetching als oplossing

Hier is de herziene versie met alleen de grammatica en spelling gecorrigeerd:

Voor het bovenstaande probleem willen we graag de gegevens voor een bepaalde pagina halen voordat deze op de website geladen wordt, zodat de gebruiker geen gegevens moet halen bij het laden van de pagina. Dit noemt men prefetching. Technisch gezien luidt de definitie als volgt:

Het is een manier om de vereiste gegevens alvorens aan te halen zodat de hoofdcomponent niet hoeft te wachten op de gegevens, waardoor de ervaring wordt verbeterd.

Dit kan de gebruikerservaring verbeteren, en de vertrouwensscore van de klant in je website verhogen.

Prefetching is een eenvoudige yet elegante oplossing die user-centric is dan een standaard proces. Om prefetching te implementeren, moeten we de gebruikersgedrag op de website begrijpen. Dat is, de meest bezochte pagina’s, of welke componenten gegevens halen bij kleine interacties (zoals een overschrijving).

Nadat zo’n scenario is geanalyseerd, is het logisch om prefetching toe te passen. Echter, als ontwikkelaars moeten we voorzichtig zijn met het gebruik van dit concept. Te veel prefetching kan ook de snelheid van uw website verminderen, omdat u probeert om veel gegevens te halen voor toekomstige scenario’s, wat wellicht de gegevenshalting voor de hoofdpagina vertraging zal geven.

Hoe Prefetching de User Experience Verbetert

Bekijk nu een paar scenario’s waarin prefetching nuttig is:

  1. Gegevens/pagina geladen voor een van de meest bezochte koppelingen van uw landingspagina. Bijvoorbeeld, denk aan een “contacteer ons” koppeling. Neem aan dat dit de koppeling is die gebruikers het meest controleren en veel gegevens bevat als die geladen wordt. In plaats van de gegevens te laden wanneer de contacteer-ons pagina wordt geladen, kun je de gegevens simpelweg halen op de startpagina zodat je niet moet wachten op de Contacteer Ons pagina voor de gegevens. U kunt meer over het voorhalen van pagina’s hier lezen.

  2. Prefetching van tabelgegevens voor latere pagina’s.

  3. Gegevens ophalen van een bovenliggende component en laden in het onderliggende component.

  4. Vooraf laden van gegevens die in een popover moeten worden weergegeven.

Dit zijn enkele manieren om voorafladen in uw toepassing te realiseren en hoe het helpt bij het verbeteren van de gebruikerservaring.

In dit blogartikel zullen we discussiëren over de laatste scenario: vooraf laden van gegevens die in de popover moeten worden weergegeven”. Dit is een klassiek voorbeeld waarbij voorafladen van nut kan zijn en een gladdere ervaring biedt aan de gebruiker.

Verder begrijpen van het probleem

Laat ik hier het probleem definiëren. Stel je het volgende scenario voor:

  1. U heeft een component dat specifieke informatie weergeeft.

  2. Er is een element binnen dit component dat een andere popover/tooltip laat zien als u er overheen zweeft.

  3. De popover haalt gegevens op bij het laden.

Stel nu dat de gebruiker over het element zweeft en moet wachten tot de gegevens zijn opgehaald en in de popover worden weergegeven. Tijdens deze wachttijd zien ze de skeletlader.

Het scenario zal er zo uit zien:

Het is maar frustrerend hoe lang de gebruiker moet wachten als ze over de afbeelding gaat:

Om dit probleem op te lossen, zijn er twee oplossingen die je kunt aanvatten en de oplossing naar eigen behoeften kunnen optimaliseren.

Oplossing #1: Prefetch gegevens in het bovenliggende component

Deze oplossing is geïnspireerd op Martin Fowler’s blogpost. Het laat u gegevens ophalen voordat het pop-upscherm verschijnt, in plaats van tijdens de component Laden.

Het pop-upscherm verschijnt wanneer u erover gaat. We kunnen de gegevens ophalen wanneer de muis de bovenliggende component binnenkomt. Voor het echte component – de afbeelding – gaat over, hebben we de gegevens voor het pop-overscherm en geven we deze door aan het pop-overcomponent.

Deze oplossing verwijdert de aanmaaktoestand niet helemaal, maar helpt om de kansen op het zien van de aanmaaktoestand significant te verlagen.

Oplossing #2: Prefetch gegevens bij pagina- Laden

Deze oplossing is geïnspireerd op x.com waar, voor het pop-overcomponent, ze de gegevens gedeeltelijk bij het hoofdgedeelte Laden en de rest van de gegevens ophalen bij component Mounten.

Zoals u kunt zien uit het bovenstaande video, worden de gebruikersprofielgegevens getoond in het pop-overscherm. Als u dichtbij kijkt, worden de gegevens over volgers later opgehaald.

Deze techniek is erg effectief wanneer je veel data moet weergeven in de popover, maar het ophalen ervan kan kostbaarder zijn op het mounten van de popover of bij het laden van de hoofdpagina.

Een betere oplossing zou zijn om de vereiste data gedeeltelijk te laden op de hoofdpagina en de rest van de data te laden wanneer het component mountt.

In ons voorbeeld haalden we de data voor de popover op wanneer de cursor deelnemer het parent-element van de afbeelding binnenging. Nu imagineer dat je extra details moet ophalen zodra de popover-data is geladen. Dus gebaseerd op de bovenstaande methode van x.com, kunnen we extra data ophalen bij het laden van de popover. Hier is het resultaat ervan:

Hier doen we het volgende:

  • We halen de hoofdgegevens op die nodig zijn om de popover te renderen wanneer de muis het parent-component van de afbeelding binnengaat.

  • Dit geeft ons genoeg tijd om de hoofdgegevens op te halen.

  • Bij het laden van de popover halen we een andere gegevens op, namelijk het aantal albums. Terwijl de gebruiker de gegevens zoals naam en e-mail leest, hebben we de volgende gegevens klaar om gezien te worden.

Op deze manier kunnen we kleine en slimme aanpassingen maken om de aanwezige loaders op het scherm zo klein mogelijk te maken 😊.

Hoe je Prefetching met React kunt implementeren.

In dit gedeelte zullen we kort de implementatie van het bovenstaande voorbeeld van vooraf laden behandelen.

Projectinstellingen

Om aan de slag te gaan met het maken van de vooraf laden app, volg de procedure hieronder:

U kunt vitejs gebruiken ( dit is wat ik gebruikte ) of create-react-app om uw app te maken. Plak de commando onderstaand in uw terminal:

yarn create vite prefetch-example --template react-ts

Nadat de app is aangemaakt, zou u de volgende mapstructuur moeten hebben als u de prefetch-example map met VS Code opent.

Nu gaan we diepgaan in de componenten die we voor deze app zullen bouwen.

Componenten

In dit voorbeeld zullen we drie componenten gebruiken:

  • PopoverExample

  • UserProfile

  • UserProfileWithFetching

PopoverExample Component

Beginnen we met de eerste component, namelijk de PopoverExample. Deze component toont een afbeeldingsavatar en wat tekst aan de rechterkant ervan. Het zou er ongeveer zo uitzien:

Het doel van deze component is om een voorbeeld te dienen dat lijkt op echte situaties. De afbeelding in deze component laadt een popovercomponent als het erover wordt gehouden.

Hier is de code voor de component:

import { useState } from "react";
import { useFloating, useHover, useInteractions } from "@floating-ui/react";
import ContentLoader from "react-content-loader";
import UserProfile from "./UserProfile";
import UserProfileWithFetching from "./UserProfileWithFetching";

export const MyLoader = () => (
    <ContentLoader
        speed={2}
        width={340}
        height={84}
        viewBox="0 0 340 84"
        backgroundColor="#d1d1d1"
        foregroundColor="#fafafa"
    >
        <rect x="0" y="0" rx="3" ry="3" width="67" height="11" />
        <rect x="76" y="0" rx="3" ry="3" width="140" height="11" />
        <rect x="127" y="48" rx="3" ry="3" width="53" height="11" />
        <rect x="187" y="48" rx="3" ry="3" width="72" height="11" />
        <rect x="18" y="48" rx="3" ry="3" width="100" height="11" />
        <rect x="0" y="71" rx="3" ry="3" width="37" height="11" />
        <rect x="18" y="23" rx="3" ry="3" width="140" height="11" />
        <rect x="166" y="23" rx="3" ry="3" width="173" height="11" />
    </ContentLoader>
);
export default function PopoverExample() {
    const [isOpen, setIsOpen] = useState(false);
    const [isLoading, setIsLoading] = useState(false);
    const [data, setData] = useState({});

    const { refs, floatingStyles, context } = useFloating({
        open: isOpen,
        onOpenChange: setIsOpen,
        placement: "top",
    });

    const hover = useHover(context);

    const { getReferenceProps, getFloatingProps } = useInteractions([hover]);

    const handleMouseEnter = () => {
        if (Object.keys(data).length === 0) {
            setIsLoading(true);
            fetch("https://jsonplaceholder.typicode.com/users/1")
                .then((resp) => resp.json())
                .then((data) => {
                    setData(data);
                    setIsLoading(false);
                });
        }
    };

    return (
        <div
            id="hover-example"
            style={{
                display: "flex",
                flexDirection: "row",
                alignItems: "center",
                textAlign: "left",
            }}
            onMouseEnter={handleMouseEnter}
        >
            <span
                style={{
                    padding: "1rem",
                }}
            >
                <img
                    ref={refs.setReference}
                    {...getReferenceProps()}
                    style={{
                        borderRadius: "50%",
                    }}
                    src="https://cdn.jsdelivr.net/gh/alohe/avatars/png/vibrent_5.png"
                />
            </span>
            <p>
                Lorem Ipsum is simply dummy text of the printing and typesetting
                industry. Lorem Ipsum has been the industry's standard dummy text ever
                since the 1500s, when an unknown printer took a galley of type and
                scrambled it to make a type specimen book. It has survived not only five
                centuries, but also the leap into electronic typesetting, remaining
                essentially unchanged. It was popularised in the 1960s with the release
                of Letraset sheets containing Lorem Ipsum passages, and more recently
                with desktop publishing software like Aldus PageMaker including versions
                of Lorem Ipsum.
            </p>
            {isOpen && (
                <div
                    className="floating"
                    ref={refs.setFloating}
                    style={{
                        ...floatingStyles,
                        backgroundColor: "white",
                        color: "black",
                        padding: "1rem",
                        fontSize: "1rem",
                    }}
                    {...getFloatingProps()}
                >
                    {isLoading ? (
                        <MyLoader />
                    ) : (
                        <UserProfile hasAdditionalDetails {...data} />
                    )}
                    {/* <UserProfileWithFetching /> */}
                </div>
            )}
        </div>
    );
}

Er gebeurt hier een aantal dingen, laat ik ze stap voor stap uitleggen:

  • We hebben een bovenliggende div genaamd hover-example die een afbeelding en wat tekst bevat.

  • Vervolgens hebben we een div conditioneel gerenderd met de klasse-naam floating. Dit is het actuele popover-component dat opens als je met de muis over de afbeelding hover.

  • Binnenin het popover hebben we conditioneel de UserProfile en de skelet-lader geladen. Deze lader verschijnt wanneer we gegevens voor het gebruikersprofiel ophalen. Meer daarover later.

  • We hebben de react-content-loader bibliotheek gebruikt in het MyLoader component. Deze bibliotheek heeft ook een website die helpt bij het maken van loaders, je kunt die hier bekijken.

Component UserProfile

Nu we ons Popover voorbeeld hebben gedefinieerd, is het aan de beurt om de details van het UserProfile component in te delen.

Dit component verschijnt binnenin het popover-component. Het doel van dit component is om de gegevens name email phone website te laden die zijn gehaald van de JSON placeholder API.

Om het voorbeeld van prefetching te demonstreren, moeten we er voor zorgen dat het UserProfile component alleen maar een presentatieve component is; dus er is geen expliciete fetching logica aanwezig binnen het.

Het belangrijkste aan dit component is dat de gegevens worden opgehaald vanuit het bovenliggende component, dat is het PopoverExample component. In dit component start je de gegevens ophalen wanneer de muis deze component binnenkomt (het mouseenter gebeuren). Dit is de oplossing #1 die we eerder besproken hadden.

Dit geeft u genoeg tijd om de gegevens te ophalen totdat de gebruiker over de afbeelding hovert. Hier is het code:

import { useEffect, useState } from "react";
import ContentLoader from "react-content-loader";

const MyLoader = () => (
    <ContentLoader
        speed={2}
        viewBox="0 0 476 124"
        backgroundColor="#d1d1d1"
        foregroundColor="#fafafa"
    >
        <rect x="4" y="43" rx="0" ry="0" width="98" height="30" />
    </ContentLoader>
);

export default function UserProfile(props: Record<string, string | boolean>) {
    const { name, email, phone, website, hasAdditionalDetails } = props;
    const [isLoading, setIsLoading] = useState(false);
    const [additionalData, setAdditionalData] = useState(0);

    useEffect(() => {
        if (hasAdditionalDetails) {
            setIsLoading(true);
            fetch("https://jsonplaceholder.typicode.com/albums")
                .then((resp) => resp.json())
                .then((data: Array<unknown>) => {
                    const albumCount = data.reduce((acc, curr) => {
                        if (curr.userId === 1) acc += 1;

                        return acc;
                    }, 0);
                    setAdditionalData(albumCount);
                })
                .finally(() => {
                    setIsLoading(false);
                });
        }
    }, [hasAdditionalDetails]);

    return (
        <div id="user-profile">
            <div id="user-name">name: {name}</div>
            <div id="user-email">email: {email}</div>
            <div id="user-phone">phone: {phone}</div>
            <div id="user-website">website: {website}</div>
            {hasAdditionalDetails && (
                <>
                    {isLoading ? (
                        <MyLoader />
                    ) : (
                        <div id="user-albums">Album Count: {additionalData}</div>
                    )}
                </>
            )}
        </div>
    );
}

Dit component maakt gebruik van de hasAdditionalDetails prop. Het doel van deze prop is om aanvullende gegevens te laden wanneer het component wordt gekoppeld. Het illustreert de oplossing #2 genoemd hierboven.

UserProfileWithFetching Component

Dit component lijkt erg op dat van het UserProfile component. Het bevat alleen de logica voor het ophalen van gegevens wanneer het component wordt geladen. Het doel van dit component is om te zien hoe de algemene oplossing zonder de prefetching techniek eruit zou zien.

Dus zal dit component altijd de gegevens laden wanneer het component wordt gekoppeld, wat de skeleton loader weergeeft.

Hier is het code:

import { useEffect, useState } from "react";
import { MyLoader } from "./PopoverExample";

export default function UserProfileWithFetching() {
    const [isLoading, setIsLoading] = useState(false);
    const [data, setData] = useState<Record<string, string>>({});

    useEffect(() => {
        setIsLoading(true);
        fetch("https://jsonplaceholder.typicode.com/users/1")
            .then((resp) => resp.json())
            .then((data) => {
                setData(data);
                setIsLoading(false);
            });
    }, []);

    if (isLoading) return <MyLoader />;

    return (
        <div id="user-profile">
            <div id="user-name">name: {data.name}</div>
            <div id="user-email">email: {data.email}</div>
            <div id="user-phone">phone: {data.phone}</div>
            <div id="user-website">website: {data.website}</div>
        </div>
    );
}

Het gehele code voor deze app kan u vinden hier.

Te veel prefetching kan ook vertraging veroorzaken

Een woord van raad, te veel prefetching is niet goed omdat:

  • Het zou uw app kunnen vertragen.

  • Het kan de gebruikerservaring verslechteren als prefetching niet strategisch wordt toegepast.

Prefetching moet worden toegepast wanneer u weet hoe de gebruiker zich verhoudt tot uw applicatie. Dit betekent dat u in staat bent de gebruikersbewegingen te voorspellen aan de hand van meetmetingen en te bepalen of ze een pagina vaak bezoeken. In dat geval is prefetching een goed idee.

Dus onthoud altijd strategisch prefetching toe te passen.

Samenvatting

Dat was het. hopelijk heeft u mijn blogpost leuk gevonden. In dit blogpost heb je geleerd dat het implementeren van prefetching de snelheid en de reactiviteit van uw webapplicatie significant kan verbeteren, waardoor de gebruikersvriendelijkheid toeneemt.

Voor verdere lezen, raadpleeg a.u.b. de onderstaande artikelen:

Voor meer inhoud kun je me volgen op Twitter, GitHub, en LinkedIn.