You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 18:22:43