Все мы сталкивались с разочарованием долгих загрузочных экранов, только чтобы оказаться в неотвечающих страницах. Видите везде иконки загрузки, но ничего не кажется двигаться вперед. Давайте я вам представлю более четкую картину:
Обычно это происходит потому, что веб-сайт пытается получить все необходимые данные как только вы попадаете на страницу. Может быть, обрабатывается запрос API, или несколько API получают данные последовательно, что приводит к задержкам в загрузке страницы.
Результат? Плохое пользовательское опыт. Вы можете подумать: “Как такая крупная компания может не приоритетизировать пользовательский опыт? Это разочаровывает.” Как результат, пользователи часто покидают сайт, что влияет на ключевые метрики и может повлиять на доход.
Но что если бы вы могли получить данные для этих тяжелых страниц заранее, так что к моменту когда пользователь попадает на страницу, он мог бы сразу же взаимодействовать с ней?
Вот тут и входит в игру концепция предзагрузки, и именно о ней мы поговорим в этой статье блога. Так что без бесполезной суеты, давайте начнем!
Содержание
Предсказательная загрузка как решение
Вот исправленная версия с учетом только грамматических и орфографических ошибок:
Чтобы решить указанную выше проблему, нам нужно загрузить данные для данной страницы до ее загрузки на сайте, чтобы пользователь не требовал данных при загрузке страницы. Это называется предсказательной загрузкой. Technically, ее определение состоит в следующем:
Это способ загрузить необходимые данные заранее, чтобы основной компонент не нуждался в дождании, тем самым улучшая опыт.
Это может улучшить пользовательский опыт, повышая доверие к вашему сайту.
Предсказательная загрузка является простым и элегантным решением, которое является более пользовательским, чем стандартный процесс. Чтобы внедрить предсказательную загрузку, нам нужно понять поведение пользователей на сайте. То есть, самые посещаемые страницы, или компоненты, загружающие данные на малые взаимодействия (например, наведение курсора).
Анализируя такие сценарии, имеет смысл применить предзагрузку к ним. Однако, как разработчики, мы должны соблюдать осторожность при использовании этого концепта. Слишком много предзагрузки также может замедлить работу вашего сайта, поскольку вы пытаетесь получить много данных для будущих сценариев, что может заблокировать получение данных для главной страницы.
Как предзагрузка улучшает пользовательское опыт
Рассмотрим несколько сценариев, где предзагрузка является выгодной:
-
Загрузка данных/страницы раньше для наиболее посещаемой ссылки с вашей стартовой страницы. Например, предположим, что у вас есть ссылка “обратиться к нам”. Допустим, что это ссылка, которую пользователи чаще всего проверяют, и при загрузке она содержит много данных. Вместо того чтобы загружать данные, когда страница “обратиться к нам” загружается, вы можете просто получить данные на главной странице, чтобы не ждать на странице “Обратиться к нам” для данных. Вы можете больше узнать о предзагрузке страниц здесь.
-
Предзагрузка таблицы данных для последующих страниц.
-
Извлечение данных из родительского компонента и загрузка их в дочерний компонент.
-
Предзагрузка данных, которые нужны для отображения в popover.
Эти методы помогают достичь предзагрузки в вашем приложении и улучшают пользовательский опыт.
В этой статье мы будем обсуждать последний сценарий: “предзагрузка данных, которые нужны для отображения в popover”. Это классический пример, где предзагрузка может быть полезна и обеспечивает более гладкий опыт для пользователя.
Understanding The Problem
Permit me to define the problem here. Imagine the following scenario:
-
You have a component that displays specific information.
-
There is an element inside this component that shows another popover/tooltip when you hover on it.
-
The popover fetches data when it loads.
Now imagine that the user hovers on the element and needs to wait for the data to be fetched and displayed in the popover. During this wait, they see the skeleton loader.
The scenario will look like this:
Очень странно, насколько долго пользователь должен ждать, если он наведет курсор на изображение:
Чтобы решить эту проблему, есть две решения, которые могут помочь вам начать и оптимизировать решение по собственным потребностям.
Решение 1: Получение данных в родительском компоненте
Это решение вдохновлено публикациейMartin Fowler. Оно позволяет вам получать данные до появления всплывающего окна, а не при загрузке компонента.
Всплывающее окно появляется, когда вы наведете курсор на него. Мы можем получать данные, когда мышь входит в родительский компонент. Перед тем, как настоящий компонент – изображение – будет наведен курсор, мы будем уже иметь данные для всплывающего окна и передадим их в компонент всплывающего окна.
Это решение не устраняет состояние загрузки полностью, но помогает значительно снизить вероятность его появления.
Решение 2: Получение данных при загрузке страницы
Это решение вдохновлено сайтом x.com, где для компонента всплывающего окна они получают данные частично при загрузке главной страницы и получают остальные данные, когда компонент устанавливается.
Как вы видите из вышедшего видео, данные профиля пользователя отображены в всплывающем окне. Если вы близко посмотреть, то данные, связанные с подписчиками, загружаются позже.
Эта техника очень эффективна, когда у вас много данных, которые нужно отобразить в Popover, но загрузка их может быть дорогой во время загрузки Popover или главной страницы.
Лучшее решение – это частично загрузить необходимые данные на главной странице и загрузить остальные данные при монтировании компонента.
В нашем примере мы загрузили данные для Popover, когда курсор входит в родительский элемент изображения. Теперь представьте себе, что вам нужно загрузить дополнительные детали после загрузки Popover данных. Таким образом, на основе вышеуказанного метода x.com, мы можем загружать дополнительные данные при загрузке Popover. Вот результат:
В этом месте мы выполняем следующие действия:
-
Мы загружаем основные данные, которые необходимы для отображения Popover, когда мышь входит в родительский компонент изображения.
-
Это gives us enough time to fetch the main data.
-
При загрузке Popover мы загружаем другие данные, которые являются количеством альбомов. Если пользователь читает данные, такие как имя и электронная почта, мы уже будем иметь следующие данные готовыми к просмотру.
Таким образом, мы можем сделать небольшие и intelligent tweaks, чтобы минимизировать появление loaders на экране 😊.
Как реализовать предозагрузку с React
В этом разделе мы быстро пройдемся по тому, как реализовать приведенный выше пример предзагрузки приложения.
Проект настройки
Для начала создания приложения предзагрузки следуйте процедуре ниже:
Вы можете использовать vitejs (это то, что я использовал) или create-react-app, чтобы создать ваше приложение. Вставьте команду, приведенную ниже, в ваш терминал:
yarn create vite prefetch-example --template react-ts
Когда приложение будет создано, у вас должна быть следующая структура папок, когда вы открываете папку prefetch-example с помощью VS Code.
Теперь пусть нам погрузимся в компоненты, которые мы собираем для этого приложения.
Компоненты
В этом примере мы будем использовать 3 компонента:
-
PopoverExample
-
UserProfile
-
UserProfileWithFetching
PopoverExample
Компонент
Пусть начнем с первого компонента, который является PopoverExample
. Этот компонент отображает изображение аватара и некоторый текст с правой стороны. Он должен выглядеть так:
Цель этого компонента – послужить примером схожего с реальными ситуациями. Изображение в этом компоненте загружает компонент попапов, когда на него наведено указатель мыши.
Здесь есть код для компонента:
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>
);
}
Здесь происходят несколько вещей, давайте разберем их шаг за шагом:
-
У нас есть родительский
div
с именемhover-example
, который содержит изображение и некоторый текст. -
Далее, мы условно отображаем
div
с именем классаfloating
. Это сам компонент всплывающего окна, который открывается при наведении курсора на изображение.- Мы воспользовались библиотекой
floating-ui
и ее базовым примером всплывающего эффекта для достижения всплывающего эффекта для всплывающего окна.
- Мы воспользовались библиотекой
-
Внутри всплывающего окна мы условно загрузили
UserProfile
и загрузчик скелета. Этот загрузчик появляется, когда мы получаем данные для профиля пользователя. Более подробно о этом позже. -
Мы воспользовались библиотекой react-content-loader в компоненте
MyLoader
. Эта библиотека также имеет веб-сайт, который помогает создавать загрузчики, вы можете проверить его здесь.
UserProfile
Компонент
Теперь, когда мы определили наш пример Popover
, пришло время погрузиться в детали компонента UserProfile
.
Этот компонент отображается внутри компонента всплывающего окна. Целью этого компонента является загрузка деталей имя
электронная почта
телефон
веб-сайт
, которые получены с API JSON placeholder.
Для демонстрации примера предзагрузки, нам надо убедиться, что компонент UserProfile
выполняет только представническую функцию; то есть, внутри него отсутствует какая-либо явная логика загрузки данных.
Основной особенностью этого компонента является то, что загрузка данных происходит из родительского компонента, которым является компонент PopoverExample
. В этом компоненте мы начинаем загрузку данных, когда мышь входит в этот компонент ( событие mouseenter
). Это решение №1, которое мы ранее讨论.
Это gives you enough time for fetching the data until the user hovers on the image. Here’s the 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>
);
}
Этот компонент использует свойство hasAdditionalDetails
. Цель этого свойства prop
состоит в загрузке дополнительных данных, когда компонент mounts. Он иллюстрирует решение №2, упомянутое выше.
Компонент UserProfileWithFetching
Этот компонент довольно схож с компонентом UserProfile
. Он содержит логику загрузки данных при загрузке компонента. Целью этого компонента является показать, какая бы общая схема решения выглядела без использования техники предзагрузки.
Таким образом, этот компонент всегда загружает данные при монтировании компонента, который отображает loader скелетон.
Вот код:
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>
);
}
Весь код для этого приложения можно найти здесь.
Также слишком много предзагрузки также может привести к замедлению
Прошлое слово, слишком много предзагрузки не хорошо, потому что:
-
Это может замедлить ваше приложение.
-
Оно может снизить качество пользовательского опыта, если предзагрузка не используется стратегически.
Предзагрузка должна применяться тогда, когда вы знаете поведение пользователя. То есть, вы можете предсказать действия пользователя по показателям и угадывать, посещает ли он страницу часто. В таком случае предзагрузка – хорошая идея.
Таким образом, помните всегда применять предзагрузку стратегически.
Резюме
Всё, что было. надеемся, вам понравилась моя статья на блоге. В этой статье вы leaned, что внедрение предзагрузки может значительно улучшить скорость и реагирование вашего веб-приложения, улучшая удовлетворение пользователей.
Для further reading, пожалуйста, смотрите на нижеarticles:
Для более подробного содержания можете подписаться за мной на Twitter, GitHub, и LinkedIn.
Source:
https://www.freecodecamp.org/news/boost-web-performance-with-prefetching/