Improve page loading performance by deferring off-screen content
Implement lazy loading for lower sections of the homepage to optimize initial render and improve user experience. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 1f7e7e89-a520-4970-9645-37daadc466dc Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 97dab47f-9107-41f0-9b7a-5900b3b486e6 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/f209e72a-0939-48fa-84fc-57854de71967/1f7e7e89-a520-4970-9645-37daadc466dc/ZApZ5Qi Replit-Helium-Checkpoint-Created: true
This commit is contained in:
parent
970c395184
commit
9a81e63740
@ -15,7 +15,7 @@ import { RecipeWidget } from "@/components/recipe-widget";
|
|||||||
import { NewsWidget } from "@/components/news-widget";
|
import { NewsWidget } from "@/components/news-widget";
|
||||||
import { SidebarWeatherWidget } from "@/components/weather-widget";
|
import { SidebarWeatherWidget } from "@/components/weather-widget";
|
||||||
import { BreakingNewsWidget } from "@/components/breaking-news-widget";
|
import { BreakingNewsWidget } from "@/components/breaking-news-widget";
|
||||||
import { useState, useEffect, useCallback, useRef, useMemo } from "react";
|
import { useState, useEffect, useCallback, useRef, useMemo, lazy, Suspense } from "react";
|
||||||
|
|
||||||
function useFocalPoints() {
|
function useFocalPoints() {
|
||||||
const { data } = useQuery<Record<string, { x: number; y: number }>>({
|
const { data } = useQuery<Record<string, { x: number; y: number }>>({
|
||||||
@ -466,6 +466,33 @@ function FeaturedCarousel({ articles, popular, galleryImages, focalPoints }: { a
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function LazySection({ children, minHeight = "200px" }: { children: JSX.Element; minHeight?: string }) {
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const el = ref.current;
|
||||||
|
if (!el) return;
|
||||||
|
const obs = new IntersectionObserver(
|
||||||
|
([entry]) => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
setVisible(true);
|
||||||
|
obs.disconnect();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ rootMargin: "300px" }
|
||||||
|
);
|
||||||
|
obs.observe(el);
|
||||||
|
return () => obs.disconnect();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={ref} style={{ minHeight: visible ? undefined : minHeight }}>
|
||||||
|
{visible ? children : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function BentoSkeleton() {
|
function BentoSkeleton() {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
@ -663,41 +690,47 @@ export default function Home() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{gridRows.map((row, ri) => (
|
{gridRows.map((row, ri) => (
|
||||||
<div key={`row-group-${ri}`}>
|
<LazySection key={`row-group-${ri}`} minHeight="280px">
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
<div>
|
||||||
{row.map((item) =>
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||||
item.type === "widget"
|
{row.map((item) =>
|
||||||
? <div key={item.key}>{item.widget!.el}</div>
|
item.type === "widget"
|
||||||
: item.type === "ad"
|
? <div key={item.key}>{item.widget!.el}</div>
|
||||||
? <div key={item.key} className="h-full" data-testid={`ad-grid-${item.key}`}><ArticleCardAd /></div>
|
: item.type === "ad"
|
||||||
: item.article
|
? <div key={item.key} className="h-full" data-testid={`ad-grid-${item.key}`}><ArticleCardAd /></div>
|
||||||
? <MediumCard key={item.key} article={item.article} focalPoints={focalPoints} />
|
: item.article
|
||||||
: null
|
? <MediumCard key={item.key} article={item.article} focalPoints={focalPoints} />
|
||||||
)}
|
: null
|
||||||
{ri === gridRows.length - 1 && widePickedArticles.length > 0 && (
|
)}
|
||||||
<div className="sm:col-span-2 lg:col-span-4 grid grid-cols-1 sm:grid-cols-2 gap-4">
|
{ri === gridRows.length - 1 && widePickedArticles.length > 0 && (
|
||||||
<WideCardClassic article={widePickedArticles[0]} focalPoints={focalPoints} />
|
<div className="sm:col-span-2 lg:col-span-4 grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
{widePickedArticles[1] && <WideCardClassic article={widePickedArticles[1]} focalPoints={focalPoints} />}
|
<WideCardClassic article={widePickedArticles[0]} focalPoints={focalPoints} />
|
||||||
</div>
|
{widePickedArticles[1] && <WideCardClassic article={widePickedArticles[1]} focalPoints={focalPoints} />}
|
||||||
)}
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</LazySection>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
<LazySection minHeight="280px">
|
||||||
{bottomSection.map((item, i) => (
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||||
<div key={`bottom-${i}`}>
|
{bottomSection.map((item, i) => (
|
||||||
{item.el}
|
<div key={`bottom-${i}`}>
|
||||||
</div>
|
{item.el}
|
||||||
))}
|
</div>
|
||||||
</div>
|
))}
|
||||||
|
</div>
|
||||||
|
</LazySection>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
<LazySection minHeight="280px">
|
||||||
<PhotoGalleryWidget key="extra-gallery" />
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||||
<NewsWidget key="extra-news" />
|
<PhotoGalleryWidget key="extra-gallery" />
|
||||||
<RecipeWidget key="extra-recipe" />
|
<NewsWidget key="extra-news" />
|
||||||
<div className="h-full"><ArticleCardAd /></div>
|
<RecipeWidget key="extra-recipe" />
|
||||||
</div>
|
<div className="h-full"><ArticleCardAd /></div>
|
||||||
|
</div>
|
||||||
|
</LazySection>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user