Next.js 13从客户端页返回SSR首页延迟问题及优化咨询
Next.js SSR首页加载状态优化与快速返回方案
1. 给SSR首页添加加载状态
针对Pages Router下使用getServerSideProps的场景,可通过以下两种方式实现加载状态:
方式一:路由事件+组件状态控制
在首页组件中监听Next.js路由切换事件,结合本地状态显示加载UI:
import { useState, useEffect } from 'react'; import { useRouter } from 'next/router'; export default function Home({ products, categories }) { const [isLoading, setIsLoading] = useState(false); const router = useRouter(); useEffect(() => { // 首次渲染完成后关闭加载状态 setIsLoading(false); // 路由切换开始时,若目标是首页则显示加载状态 const handleRouteStart = () => { if (router.pathname === '/') setIsLoading(true); }; // 路由切换完成后关闭加载状态 const handleRouteComplete = () => { if (router.pathname === '/') setIsLoading(false); }; router.events.on('routeChangeStart', handleRouteStart); router.events.on('routeChangeComplete', handleRouteComplete); // 组件卸载时移除监听 return () => { router.events.off('routeChangeStart', handleRouteStart); router.events.off('routeChangeComplete', handleRouteComplete); }; }, [router]); // 加载状态下显示骨架屏或加载动画 if (isLoading) { return ( <div className="loading-container"> <div className="skeleton-home"></div> </div> ); } // 正常渲染首页内容 return ( <div> {/* 渲染products和categories数据 */} </div> ); } export async function getServerSideProps() { // 并行调用两个API,减少服务器端等待时间 const [productsRes, categoriesRes] = await Promise.all([ fetch('https://your-api.com/products'), fetch('https://your-api.com/categories') ]); const products = await productsRes.json(); const categories = await categoriesRes.json(); return { props: { products, categories } }; }
方式二:优化服务器端API调用
先通过Promise.all并行执行getServerSideProps中的两个API请求,从根源上缩短服务器端处理时间,减少加载延迟。
2. 实现从客户端页面快速返回SSR首页
核心思路是缓存首页的渲染数据或内容,避免返回时重复调用API和重新渲染,以下是几种可行方案:
方案一:客户端缓存服务器端Props
将首页的初始props存入sessionStorage,返回时优先读取缓存渲染,同时后台静默更新最新数据:
import { useState, useEffect } from 'react'; import { useRouter } from 'next/router'; export default function Home(initialProps) { const [props, setProps] = useState(initialProps); const [isRefreshing, setIsRefreshing] = useState(false); const router = useRouter(); useEffect(() => { // 首次渲染时缓存初始props sessionStorage.setItem('homeProps', JSON.stringify(initialProps)); // 页面重新可见时自动刷新数据(可选) const handleVisibilityChange = async () => { if (document.visibilityState === 'visible' && router.pathname === '/') { setIsRefreshing(true); const [productsRes, categoriesRes] = await Promise.all([ fetch('https://your-api.com/products'), fetch('https://your-api.com/categories') ]); const newProps = { products: await productsRes.json(), categories: await categoriesRes.json() }; setProps(newProps); sessionStorage.setItem('homeProps', JSON.stringify(newProps)); setIsRefreshing(false); } }; document.addEventListener('visibilitychange', handleVisibilityChange); return () => document.removeEventListener('visibilitychange', handleVisibilityChange); }, [initialProps, router]); // 返回首页时优先读取缓存 useEffect(() => { if (router.pathname === '/') { const cachedProps = sessionStorage.getItem('homeProps'); if (cachedProps) setProps(JSON.parse(cachedProps)); } }, [router]); return ( <div> {/* 渲染props数据 */} {isRefreshing && <div className="refresh-tip">正在更新最新数据...</div>} </div> ); } // 保持原有的getServerSideProps逻辑 export async function getServerSideProps() { const [productsRes, categoriesRes] = await Promise.all([ fetch('https://your-api.com/products'), fetch('https://your-api.com/categories') ]); const products = await productsRes.json(); const categories = await categoriesRes.json(); return { props: { products, categories } }; }
方案二:用router.back()替代普通跳转
在商品详情页的返回按钮中使用router.back(),利用浏览器页面缓存实现快速返回:
import { useRouter } from 'next/router'; export default function ProductDetail() { const router = useRouter(); const handleBack = () => { // 回到上一页,浏览器优先使用缓存的首页 router.back(); }; return ( <button onClick={handleBack}>返回首页</button> ); }
方案三:服务器端设置缓存头
在getServerSideProps中配置响应缓存头,让浏览器缓存首页HTML,减少重复请求:
export async function getServerSideProps({ res }) { // 设置缓存策略:缓存5分钟,过期后可先返回旧数据再异步更新 res.setHeader( 'Cache-Control', 'public, s-maxage=300, stale-while-revalidate=600' ); const [productsRes, categoriesRes] = await Promise.all([ fetch('https://your-api.com/products'), fetch('https://your-api.com/categories') ]); const products = await productsRes.json(); const categories = await categoriesRes.json(); return { props: { products, categories } }; }
内容的提问来源于stack exchange,提问作者prawar mundra
相关产品推荐
相关产品推荐

