- Вопрос или проблема
- Ответ или решение
- 1. Архитектура приложения и рендеринг страниц
- Используйте предварительную выборку данных
- Префетчинг
- 2. Оптимизация компонента PageContent
- Использование метода useMemo
- Lazy Loading
- 3. Оптимизация API-запросов
- Использование кэша
- 4. Настройка современных подходов к маршрутизации
- Анимация переходов
- 5. Анализ производительности
- Используйте инструменты мониторинга
- Заключение
Вопрос или проблема
Страницы загружаются медленно/перенаправление не происходит немедленно после того, как я нажимаю на ссылку, чтобы открыть динамически сформированные страницы.
*это приложение/appliance-service/[slug]/page.tsx, которое я пытаюсь открыть
*
import React, { Suspense } from 'react';
import PageContent from './PageContent';
import { LocalBusiness, WebSite, SearchAction, PostalAddress, GeoCoordinates, Person, Organization, OpeningHoursSpecification, FAQPage, WithContext } from 'schema-dts';
import SchemaScripts from './SchemaScripts';
import MetadataGenerator from './MetadataGenerator';
interface PageParams {
slug: string;
}
// Объекты JSON-LD Schema.org
const localBusinessJsonLd: WithContext<LocalBusiness> = {
временно удалено
}
// Вспомогательные функции для получения данных
const Page = async ({ params }: { params: PageParams }) => {
return (
<>
<PageContent slug={params.slug} />
{/* Скрипты схемы */}
<SchemaScripts
slug={params.slug}
localBusinessJsonLd={localBusinessJsonLd}
websiteJsonLd={websiteJsonLd}
personJsonLd={personJsonLd}
organizationJsonLd={organizationJsonLd}
/>
</>
);
}
export async function generateMetadata({ params }: { params: PageParams }) {
return await MetadataGenerator({ slug: params.slug });
}
export default Page;
это компонент приложения/appliance-service/PageContent, который используется в вышеупомянутом page.tsx
"use client";
import React, { useMemo, useRef } from 'react';
import useSWR from 'swr';
import RefrigeratorRepairBanner from '../../components/appliance-service-ui/Banner';
import ServiceFeatures from '../../components/appliance-service-ui/ServiceFeature';
import ApplianceFaq from '@/app/components/appliance-service-ui/ApplianceFaq';
import ContactSection from '../../components/appliance-service-ui/ContactSection';
import ServiceCenterLocation from "@/app/components/appliance-service-ui/ServiceCenterLocation";
import ApplianceCard from '@/app/components/ApplianceCard';
import WhyChooseUs from "@/app/components/appliance-service-ui/WhyChooseUs";
import { ImageOnLeft } from "@/app/components/appliance-service-ui/ImageOnLeft";
import { ImageOnRight } from "@/app/components/appliance-service-ui/ImageOnRight";
import { ProblemSection } from "@/app/components/appliance-service-ui/ProblemSection";
import { RepairParts } from "@/app/components/appliance-service-ui/RepairParts";
import { TypesOfAppliance } from "@/app/components/appliance-service-ui/TypesOfAppliance";
import LogoSliderAppliance from '@/app/components/appliance-service-ui/LogoSliderAppliance';
import ServiceAreasAppliance from '@/app/components/appliance-service-ui/ServiceAreaAppliance';
import NotFoundPage from '@/app/not-found';
import BannerSkeleton from './components/RefrigeratorRepairBannerSkeleton';
interface PageContentProps {
slug: string;
}
type ComponentType =
| "TypesOfAppliance"
| "ImageOnLeft"
| "ImageOnRight"
| "ProblemSection"
| "RepairParts"
| "ContactSection";
// Функция для получения данных
const fetcher = (url: string) => fetch(url, { cache: 'no-cache' }).then(res => {
if (!res.ok) {
throw new Error('Ответ сети не был успешным');
}
return res.json();
});
const PageContent: React.FC<PageContentProps> = ({ slug }) => {
const { data: pageData, error: pageError } = useSWR(`/api/pages/${slug}`, fetcher);
const { data: locationsData } = useSWR(`/api/pages/items`, fetcher);
const { data: slugsData } = useSWR(`/api/pages`, fetcher);
const loading = !pageData && !pageError; // Состояние загрузки
const notFound = pageError && pageError.message === '404'; // Состояние не найдено
const decodedJoditContent = useRef('');
// Декодирование содержимого Jodit
if (pageData && pageData.content) {
decodedJoditContent.current = pageData.content;
}
// Мемоизируем карту компонентов, чтобы избежать ее пересоздания на каждом рендере
const componentMap = useMemo(() => ({
TypesOfAppliance,
ImageOnLeft,
ImageOnRight,
ProblemSection,
RepairParts,
ContactSection,
}), []);
// Мемоизируем функцию getComponentByType
const getComponentByType = useMemo(() => (type: ComponentType) => {
return componentMap[type] || null;
}, [componentMap]);
// Мемоизируем isEmptyContent, чтобы предотвратить ненужные перерасчеты
const isEmptyContent = useMemo(() => (content: string) => {
return content.trim() === '<p><br></p>' || content.trim() === '';
}, []);
// Обрабатываем состояния загрузки и не найдено
if (loading) return <BannerSkeleton />;
if (notFound) return <NotFoundPage />;
// Убедитесь, что pageData определено перед деструктурированием
if (!pageData) {
return <NotFoundPage />;
}
const locations = locationsData?.locations.map((location: { name: string }) => location.name) || [];
const slugs = slugsData?.map((page: { slug: string }) => page.slug) || [];
// Деструктурируем свойства из pageData безопасно
const { banner, components, faqs, appliance, whyChooseUs, brandName } = pageData || {};
return (
<div>
<RefrigeratorRepairBanner
heading={banner.title}
imageSrc={banner.imageUrl}
description={banner.description}
alt={banner.alt}
/>
<ServiceFeatures />
<WhyChooseUs
appliance={appliance}
sectionHeading={whyChooseUs.sectionHeading}
sectionContent={whyChooseUs.sectionContent}
expertiseTitle={whyChooseUs.expertiseTitle}
expertiseDescription={whyChooseUs.expertiseDescription}
availabilityTitle={whyChooseUs.availabilityTitle}
availabilityDescription={whyChooseUs.availabilityDescription}
techniciansTitle={whyChooseUs.techniciansTitle}
techniciansDescription={whyChooseUs.techniciansDescription}
/>
<div className="max-w-7xl 2xl:container mx-auto grid grid-cols-1 lg:grid-cols-[30%_70%] mt-6">
<div className="order-last lg:order-first w-full">
<ServiceCenterLocation
brandName={brandName}
appliance={appliance}
location={locations}
slugs={slugs}
/>
</div>
<div className="flex-grow overflow-y-auto">
{!isEmptyContent(decodedJoditContent.current) && (
<div
className="jodit-content"
dangerouslySetInnerHTML={{ __html: decodedJoditContent.current }}
/>
)}
{components?.map((component: { type: string; _id: any; props: any }) => {
const Component = getComponentByType(component.type as ComponentType);
return Component ? <Component key={component._id} {...component.props} /> : null;
})}
<ServiceAreasAppliance location={locations} appliance={appliance} />
</div>
</div>
<LogoSliderAppliance appliance={appliance} />
<div className="max-w-7xl 2xl:container mx-auto">
<ApplianceFaq faqs={faqs} />
</div>
<ApplianceCard />
</div>
);
};
export default PageContent;
Когда я нажимаю на ссылки в навигационной панели, чтобы открыть эту страницу, это занимает много времени. Я понимаю, что требуется время для загрузки страницы, данные для которой поступают из бэкенда, но это перенаправление не происходит мгновенно, мне нужно почти 2 секунды просто для перенаправления на этот URL.
Буду признателен за любую помощь.
Ответ или решение
Ситуация, когда переход по ссылке на странице приложения Next.js занимает 3-4 секунды, может быть результатом ряда факторов, как технических, так и связанных с архитектурой вашего приложения. Давайте рассмотрим основные элементы, которые могут влиять на скорость загрузки, и предложим возможные решения.
1. Архитектура приложения и рендеринг страниц
Используйте предварительную выборку данных
Ваша страница (page.tsx
) выполняет асинхронные запросы к API для загрузки данных. Это дело, конечно, важно, однако ждать ответа от сервера может занять время. Вы можете использовать метод getStaticProps
или getServerSideProps
, если в этом есть смысл для вашего случая. Это позволит получить данные заранее, что может значительно ускорить время отклика по сравнению с тем, когда данные загружаются после инициализации компонента.
Префетчинг
Если данные, необходимые для отображения страницы, часто не меняются, подумайте о предзагрузке данных (prefetch), которая может помочь ускорить время загрузки. Эта функциональность Next.js позволит загружать данные до взаимодействия пользователя с ссылкой.
2. Оптимизация компонента PageContent
Использование метода useMemo
Вы уже используете useMemo
для кэширования результатов, и это хорошая практика. Однако убедитесь, что вы эффективно используете эту оптимизацию. Проверьте, не является ли это потенциальной причиной для лишних вычислений при каждом перерисовывании.
Lazy Loading
Подумайте о использовании ленивой загрузки (React.lazy
и Suspense
) для компонентов, которые не нужны сразу на загрузке. Это поможет разбить ваш код на меньшие части и загружать только необходимые элементы по мере необходимости, тем самым уменьшая первоначальный размер загрузки.
3. Оптимизация API-запросов
Использование кэша
В вашем коде используется useSWR
с параметром cache: 'no-cache'
, что приводит к тому, что каждый раз осуществляется новый запрос к серверу. Настройка кэша для данных поможет избежать лишних запросов. Мы рекомендуем использовать подход, при котором данные хранятся на стороне клиента и обновляются только при необходимости (например, каждые несколько минут или при изменении данных).
4. Настройка современных подходов к маршрутизации
Анимация переходов
Добавьте анимации переходов или спиннеры. Это поможет улучшить восприятие задержки. Если пользователь видит интерфейсный элемент, показывающий прогресс, это может компенсировать задержку в переходах.
5. Анализ производительности
Используйте инструменты мониторинга
Используйте инструменты анализа производительности, такие как Lighthouse, чтобы выявить узкие места и проблемы, которые могут замедлять загрузку. Это даст вам представление о том, какие элементы приложения требуют оптимизации.
Заключение
Скорость загрузки страниц — это критически важный аспект пользовательского опыта, и улучшение его должно стать частью вашей стратегии разработки. Применяя описанные выше подходы, вы сможете ускорить переходы между страницами и обеспечить пользователям комфортное взаимодействие с вашим приложением.
Не забудьте протестировать изменения и следовать за метриками производительности, чтобы убедиться, что внесенные улучшения работают на практике.