Next.js Router响应缓慢问题排查与优化方案咨询
你的路由切换慢确实大概率是getServerSideProps导致的——这个函数会每次用户访问首页(包括从其他路由切过来)时,都在服务器端发起一次API请求,请求完成后才会返回页面,自然拖慢了响应速度。不需要替换成React Router,Next.js自带的路由系统本身是高效的,核心问题在数据获取逻辑,给你几个针对性的修改方案:
方案一:改用静态生成(SSG)+ 增量静态再生(ISR)(推荐)
如果你的视频数据不需要实时更新(比如允许5-10分钟的缓存),这个方案最快,页面会提前生成并缓存,后续请求直接返回静态文件。
修改首页的getServerSideProps为getStaticProps,并加上revalidate参数实现自动更新:
// 首页代码修改后的getStaticProps export const getStaticProps = async () => { const response = await axios.get(`${BASE_URL}/api/post`); return { props: { videos: response.data }, revalidate: 300, // 每5分钟(300秒)重新生成一次页面 }; };
如果需要支持topic查询参数的动态路由,把首页改成动态路由(比如pages/[topic].tsx),再配合getStaticPaths预生成常见页面:
// 动态路由页面的getStaticPaths export const getStaticPaths = async () => { // 先获取所有可能的topic列表 const topicsRes = await axios.get(`${BASE_URL}/api/discover/topics`); const paths = topicsRes.data.map((topic: string) => ({ params: { topic }, })); return { paths, fallback: 'blocking', // 未预生成的topic会在首次访问时生成并缓存 }; }; export const getStaticProps = async ({ params }: { params: { topic: string } }) => { const { topic } = params; let response; if(topic) { response = await axios.get(`${BASE_URL}/api/discover/${topic}`); } else { response = await axios.get(`${BASE_URL}/api/post`); } return { props: { videos: response.data }, revalidate: 300, }; };
方案二:改用客户端数据获取
如果数据必须实时更新,把请求移到组件内部,用useEffect或者数据缓存库(比如SWR)实现,路由切换时页面先渲染加载状态,数据加载完成后再更新,用户感知更流畅,还能自动缓存请求结果。
用useEffect实现:
// 修改后的首页组件 import axios from 'axios'; import { useState, useEffect } from 'react'; import VideoCard from '../components/VideoCard'; import { BASE_URL } from '../utils'; import { Video } from '../types'; import NoResults from '../components/NoResults'; const Home = () => { const [videos, setVideos] = useState<Video[]>([]); const [loading, setLoading] = useState(true); useEffect(() => { const fetchVideos = async () => { const urlParams = new URLSearchParams(window.location.search); const topic = urlParams.get('topic'); let response; if(topic) { response = await axios.get(`${BASE_URL}/api/discover/${topic}`); } else { response = await axios.get(`${BASE_URL}/api/post`); } setVideos(response.data); setLoading(false); }; fetchVideos(); }, []); if(loading) { return <div className="flex items-center justify-center h-full">加载中...</div>; // 可替换为骨架屏 } return ( <div className='flex flex-col gap-10 videos h-full'> {videos.length ? videos?.map((video: Video) => ( <VideoCard post={video} isShowingOnHome key={video._id} /> )) : <NoResults text={`No Videos`} />} </div> ); }; export default Home;
用SWR优化(推荐,自带缓存、重试等功能)
先安装SWR:npm install swr
import axios from 'axios'; import useSWR from 'swr'; import VideoCard from '../components/VideoCard'; import { BASE_URL } from '../utils'; import { Video } from '../types'; import NoResults from '../components/NoResults'; const fetcher = async (url: string) => { const response = await axios.get(url); return response.data; }; const Home = () => { const urlParams = new URLSearchParams(window.location.search); const topic = urlParams.get('topic'); const apiUrl = topic ? `${BASE_URL}/api/discover/${topic}` : `${BASE_URL}/api/post`; const { data: videos, error, isLoading } = useSWR(apiUrl, fetcher); if(isLoading) { return <div className="flex items-center justify-center h-full">加载中...</div>; } if(error) { return <div className="flex items-center justify-center h-full">加载失败,请重试</div>; } return ( <div className='flex flex-col gap-10 videos h-full'> {videos?.length ? videos?.map((video: Video) => ( <VideoCard post={video} isShowingOnHome key={video._id} /> )) : <NoResults text={`No Videos`} />} </div> ); }; export default Home;
方案三:优化getServerSideProps(临时过渡方案)
如果暂时不想改数据获取方式,可以给API请求加内存缓存,减少重复请求:
export const getServerSideProps = async ({ query: { topic }, }: { query: { topic: string }; }) => { const cacheKey = topic ? `topic_${topic}` : 'home_videos'; // 内存缓存1分钟(生产环境建议用Redis等持久化缓存) if((global as any)[cacheKey] && Date.now() - (global as any)[cacheKey].timestamp < 60000) { return { props: { videos: (global as any)[cacheKey].data }, }; } let response; if(topic) { response = await axios.get(`${BASE_URL}/api/discover/${topic}`); } else { response = await axios.get(`${BASE_URL}/api/post`); } (global as any)[cacheKey] = { data: response.data, timestamp: Date.now(), }; return { props: { videos: response.data }, }; };
另外,你的侧边栏Link组件使用是正确的,Next.js的Link会自动做预加载优化(用户hover链接时提前加载页面),不需要额外调整路由系统。
内容的提问来源于stack exchange,提问作者bruceyyy_12
相关产品推荐
相关产品推荐

