Todos nós já tivermos a frustração de esperar através de telas de carregamento longas, apenas para descobrirmos que estamos presos com páginas inativas. Você vê spinner de carregamento em todo lugar, mas nada parece avançar. Deixe-me pintar uma imagem mais clara para você:
Isso normalmente acontece porque o site está tentando obter todos os dados necessários assim que você chega na página. Pode ser que uma solicitação API esteja sendo processada ou que várias APIs estão buscando dados sequencialmente, causando atrasos no carregamento da página.
O resultado? Uma má experiência do usuário. Você pode pensar: “Como é que uma empresa tão grande não prioriza a experiência do usuário? Isto é decepcionante.” Portanto, os usuários frequentemente deixam o site, afetando as métricas chave e potencialmente impactando o rendimento.
Mas e se você pudesse obter os dados para essas páginas pesadas com antelha, para que quando o usuário chegar na página, ele possa interagir com ela instantaneamente?
É aqui que entra o conceito de prefetching, e é exatamente aquilo que vamos mergulhar neste post de blog. Então, sem mais ado, vamos começar!
Conteúdo da Tabela
Prefetching como Solução
Veja a versão revisada apenas com correções de gramática e ortografia:
Para o problema acima, o que queremos é buscar dados para uma página dada antes de ela ser carregada no site, para que o usuário não precise buscar dados na hora do carregamento da página. Isso é chamado de prefetching. De forma técnica, sua definição é a seguinte:
É uma maneira de buscar os dados necessários antecipadamente para que o componente principal não precise esperar pelos dados, melhorando a experiência.
Isso pode melhorar a experiência do usuário, aumentando a confiança do cliente no seu site.
O prefetching é uma solução simples e elegante que é mais usuário-centrada do que um processo padrão. Para implementar o prefetching, precisamos entender o comportamento do usuário no site. Isso é, as páginas mais visitadas ou quais componentes buscam dados em pequenas interações (como passar o mouse por cima).
Após analisar tais cenários, faz sentido aplicar o prefetching a eles. No entanto, como desenvolvedores, devemos ter cuidado com o uso deste conceito. Demasiado prefetching pode também lentificar o seu site, já que você está tentando buscar muitos dados para cenários futuros, o que pode bloquear a busca de dados para a página principal.
Como o Prefetching Melhora a Experiência do Usuário
Vamos olhar para alguns cenários onde o prefetching é benéfico:
-
Carregando dados/página mais cedo para o link mais visitado de sua página de destino. Por exemplo, considere que você tem um link “fale conosco”. Suponha que este é o link que os usuários mais verificam e que contém muitos dados quando carrega. Em vez de carregar os dados quando a página “fale conosco” carrega, você pode simplesmente buscar os dados na página inicial para que você não precise esperar na página “Fale Conosco” para os dados. Você pode ler mais sobre o prefetching de páginas aqui.
-
Prefetching de dados de tabela para páginas posteriores.
-
Está carregando dados the um componente pai e o carregando no componente filho.
-
Está prefetching dados que precisam ser exibidos num popover.
Estes são algumas das formas de conseguir prefetching em sua aplicação e como isso ajuda a melhorar a experiência do usuário.
Neste post do blog iremos discutir sobre o último cenário: “prefetching dados que precisam ser exibidos no popover”. Este é um exemplo clássico onde o prefetching pode ser benéfico e fornece uma experiência mais suave ao usuário.
Entendendo o Problema
Deixe-me definir o problema aqui. Imagine o seguinte cenário:
-
Você tem um componente que exibe informações específicas.
-
Há um elemento dentro deste componente que mostra outro popover/tooltip quando você passa o mouse sobre ele.
-
O popover carrega dados quando é carregado.
Agora imaginem que o usuário passa o mouse sobre o elemento e precisa esperar para os dados serem carregados e exibidos no popover. Durante esse tempo de espera, eles veem o carregador de esqueleto.
O cenário terá este aspecto:
É frustrante saber que o usuário tem que esperar muito tempo sempre que passa o mouse sobre a imagem:
Para resolver esse problema, existem duas soluções que podem ajudar você a começar e personalizar a solução de acordo com suas necessidades.
Solução #1: Pré-carregar Dados no Componente Pai
Esta solução é inspirada em um post do blog de Martin Fowler. Ela permite que você carregue os dados antes da janela de popup aparecer, em vez de carregá-los quando o componente é carregado.
A janela de popup aparece quando você passa o mouse sobre ela. Nós podemos carregar os dados quando o mouse entra no componente pai. Antes do componente real—a imagem—ser passado com o mouse, temos os dados para a janela popover e os passamos para o componente popover.
Esta solução não remove o estado de carregamento completamente, mas ajuda a reduzir significativamente as chances de ver o estado de carregamento.
Solução #2: Pré-carregar Dados na Carregamento da Página
Esta solução é inspirada em x.com onde, para o componente de popover, eles carregam os dados parcialmente no carregamento principal da página e carregam o restante dos dados quando o componente é montado.
Como você pode ver no vídeo acima, os detalhes do perfil do usuário são exibidos na janela popover. Se você olhar de perto, os detalhes relacionados aos seguidores são carregados posteriormente.
Essa técnica é altamente eficiente quando você tem muitos dados para serem exibidos no popover, mas buscá-los pode ser caro no momento do montagem do popover ou na carga da página principal.
Uma solução melhor seria carregar parcialmente os dados necessários na página principal e carregar o restante dos dados quando o componente for montado.
No nosso exemplo, buscámos os dados para o popover quando o cursor entrou no elemento pai da imagem. Agora imagina que você precisa buscar detalhes adicionais assim que os dados do popover forem carregados. Portanto, com base na metodologia do x.com acima, podemos buscar dados adicionais na hora do carregamento do popover. Aqui está o resultado disso:
Aqui, nós fazemos as seguintes coisas:
-
Nós buscamos os dados principais que são necessários apenas para renderizar o popover quando o rato entra no componente pai da imagem.
-
Isto dá tempo suficiente para buscar os dados principais.
-
Ao carregar o popover, nós buscamos outros dados, que é o número de álbuns. Enquanto o usuário lê dados como nome e e-mail, nós teríamos os próximos dados prontos para serem vistos.
Dessa forma, podemos fazer pequenas e inteligentes melhorias para minimizar o tempo de carregamento de loaders na tela 😊.
Como Implementar Prefetching com React
Nesta seção, nós passaremos rápido por como implementar o exemplo de aplicação de prefetching acima.
Configuração do Projeto
Para começar a criar a aplicação de prefetching, siga com o processo abaixo:
Você pode usar vitejs (isto é o que eu usei) ou create-react-app para criar sua aplicação. Copie o comando abaixo no seu terminal:
yarn create vite prefetch-example --template react-ts
Uma vez que a aplicação tenha sido criada, você deveria ter a seguinte estrutura de pasta quando abrir a pasta prefetch-example com o VS Code.
Agora vamos mergulhar nas componentes que vamos construir para esta aplicação.
Componentes
Neste exemplo, vamos usar 3 componentes:
-
PopoverExample
-
UserProfile
-
UserProfileWithFetching
PopoverExample
Componente
Vamos começar com o primeiro componente, que é o PopoverExample
. Este componente exibe uma imagem de avatar e algum texto ao lado direito dela. Deveria parecer assim:
O propósito deste componente é servir de exemplo semelhante a situações reais da vida. A imagem neste componente carrega um componente de popover quando é passado sobre.
Aqui está o código do componente:
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>
);
}
Há algumas coisas acontecendo aqui, deixe-me explicar passo a passo:
-
Temos um
div
pai chamadohover-example
que contém uma imagem e algum texto. -
A seguir, nós renderizamos condicionalmente um
div
com o nome de classefloating
. Este é o componente de popover real que é aberto quando você passa o mouse sobre a imagem.- Nós usamos a
floating-ui
library e seu exemplo básico de hover para alcançar o efeito de hover para o popover.
- Nós usamos a
-
Dentro do popover, nós carregamos condicionalmente o
UserProfile
e o carregador de esqueleto. Este carregador aparece quando estamos buscando dados para o perfil do usuário. Mais sobre isso depois. -
Nós usamos a biblioteca react-content-loader no componente
MyLoader
. Esta biblioteca também tem um site que ajuda a criar carregadores, você pode ver aqui.
Componente UserProfile
Agora que definimos o exemplo de Popover
, é hora de nos avançar nos detalhes do componente UserProfile
.
Este componente aparece dentro do componente popover. O propósito deste componente é carregar os detalhes name
email
phone
website
, que são obtidos do JSON placeholder API.
Para demonstrar o exemplo de prefetching, temos que certificar que o componente UserProfile
apenas age como um componente de apresentação; isto é, não há nenhuma lógica de busca explicita dentro dele.
O ponto chave que deve ser notado sobre este componente é que a busca dos dados acontece a partir do componente pai, que é o componente PopoverExample
. Neste componente, começamos a buscar os dados quando o mouse entra neste componente (o evento mouseenter
). Esta é a solução #1 que discutimos anteriormente.
Isto dá tempo suficiente para buscar os dados até o usuário passar o mouse sobre a imagem. Aqui está o código:
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>
);
}
Este componente faz uso da propriedade hasAdditionalDetails
. O objetivo desta prop
é carregar dados adicionais quando o componente é montado. Ela ilustra a solução #2 mencionada acima.
Componente UserProfileWithFetching
Este componente é muito semelhante ao componente UserProfile
. Ele apenas contém a lógica para buscar dados quando o componente é carregado. O objetivo deste componente é mostrar qual seria a solução geral sem a técnica de prefetching.
Então, este componente sempre carregará os dados quando o componente for montado, o que exibe o carregador de esqueleto.
Aqui está o código:
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>
);
}
O código inteiro desta aplicação pode ser encontrado aqui.
Muito prefetching pode também causar lentidão
Um aviso, muito prefetching não é bom porque:
-
Pode lentificar sua aplicação.
-
Isso pode degrada a experiência do usuário se a previsão não for aplicada estratégicamente.
A previsão precisa ser aplicada quando você sabe o comportamento do usuário. Ou seja, você é capaz de prever o movimento do usuário por meio de métricas e saber se eles visitam uma página frequentemente. Nesse caso, a previsão é uma boa ideia.
Então, lembre-se de aplicar sempre a previsão estratégicamente.
Resumo
Essa é tudo, pessoal! Espero que você goste do meu post no blog. Neste post do blog, você aprender que a implementação de previsão pode melhorar significativamente a velocidade e a responsividade de sua aplicação web, melhorando a satisfação do usuário.
Para leituras adicionais, por favor, consulte os artigos abaixo:
Para mais conteúdo, você pode seguir comigo em Twitter, GitHub, e LinkedIn.
Source:
https://www.freecodecamp.org/news/boost-web-performance-with-prefetching/