如何在Next.js中使用getStaticPaths实现CatchAll路由?
Next.js中getStaticProps与CatchAll路由结合的问题
我尝试用getStaticProps和getStaticPaths在构建阶段生成页面,这是第一次处理CatchAll路由的需求。原本想把URL里的params id从getStaticProps传到getStaticPaths,通过Info页面id获取对应剧集id,但逻辑跑不通,现在代码没法正常运行,访问watch页面会无限循环导致页面冻结。
路由路径:/watch/[...params]
export const getStaticPaths = async(props) => { // This Code Doesn't Work const {infoId, watchId, gogoInfoId, gogoWatchId} = props if(infoId){ // Fetch Info Page And Get Episodes Ids Which Will Be Displayed As Paths const req = await axios.get(process.env.API_URL + `/info/${infoId}`) const path = req.data?.episodes?.map((items) => { return {params: {params: [infoId.toString(), items.id.toString()]}} }) return{ paths: path, fallback: false, } } else if(gogoInfoId){ // Fetch Info Page And Get Episodes Ids Which Will Be Displayed As Paths const req = await axios.get(process.env.API_URL_GOGO + `/info/${gogoInfoId}`) const path = req.data?.episodes?.map((items) => { return {params: {params: [infoId.toString(), items.id.toString()]}} }) return{ paths: path, fallback: false, } } } export const getStaticProps = async(context) => { // Get The Params From The Query In The CatchAll Url const {params} = context.query const infoId = params[0] const watchId = params[1] // Main API URLs const infoUrl = process.env.API_URL + `/info/${infoId}` const watchUrl = process.env.API_URL + `/watch/${watchId}` // Backup API const infoUrlEnime = process.env.API_URL_GOGO + `/info/${infoId}` const watchUrlEnime = process.env.API_URL_GOGO + `/watch/${watchId}` try{ // Checking If Id/params Doesn't Contain Letters if(/^\d+$/.test(infoId)){ // Fetch From the Main API const req1 = await axios.get(infoUrl) const req2 = await axios.get(watchUrl) return{ props: { infoData: req1.data, watchData: req2.data, infoId: infoId, watchId: watchId, } } } else{ // Fetch From the Backup API const req3 = await axios.get(infoUrlEnime) const req4 = await axios.get(watchUrlEnime,{ params: { server: "vidstreaming" } }) return{ props: { infoData: req3.data, watchData: req4.data, gogoInfoId: infoId, gogoWatchId: watchId, } } } }catch(err){ // Just A Custom Redirection If Something Went Wrong return serversDown(err) } }
路由示例:http://localhost:3000/watch/142853/tokyo-revengers-seiya-kessen-hen-episode-4
API Schema示例(/info/142853)
{ "id": "142853", ... "episodes": [ { "id": "tokyo-revengers-seiya-kessen-hen-episode-1", ... }, { "id": "tokyo-revengers-seiya-kessen-hen-episode-2", ... }, ... ] }
问题分析
- 核心逻辑错误:
getStaticPaths无法接收getStaticProps返回的props。这两个函数是独立执行的:getStaticPaths在构建阶段先运行,用来定义需要预渲染的路径;之后才会为每个路径执行getStaticProps。你试图从getStaticProps传参到getStaticPaths的思路完全不成立。 - 无限循环原因:
getStaticPaths中没有合法的参数输入,导致返回的paths为空,加上fallback: false,Next.js会返回404。如果你的serversDown错误处理函数存在重定向到当前页面的逻辑,就会触发无限循环。 - 其他代码问题:
getStaticPaths的参数不是props,而是Next.js提供的context对象,你在这里解构的参数根本不存在。- 第二个分支中生成路径时误用了
infoId,应该是gogoInfoId。 - CatchAll路由的参数应该从
context.params获取,而非context.query(后者是客户端路由的参数)。
解决方案
根据你的需求,有两种实现思路:
思路1:提前预渲染所有可能的剧集页面
如果能提前获取所有infoId列表,可以在构建阶段遍历所有infoId,拉取对应剧集生成路径:
export const getStaticPaths = async () => { // 假设你有接口能获取所有infoId列表 const allInfoIds = await axios.get(process.env.API_URL + '/all-info-ids').then(res => res.data); // 遍历所有infoId,生成对应的剧集路径 const paths = await Promise.all( allInfoIds.map(async (infoId) => { const res = await axios.get(process.env.API_URL + `/info/${infoId}`); return res.data.episodes.map(episode => ({ params: { params: [infoId.toString(), episode.id.toString()] } })); }) ).then(flatArr => flatArr.flat()); return { paths, fallback: false }; };
思路2:动态生成页面(适合infoId数量过多的场景)
如果无法提前获取所有infoId,可以使用fallback: 'blocking',让Next.js在用户访问时动态生成页面并缓存:
export const getStaticPaths = async () => { // 返回空paths,所有路径都在请求时动态生成 return { paths: [], fallback: 'blocking' // 或true,根据缓存需求选择 }; }; export const getStaticProps = async (context) => { // 修正:从context.params获取CatchAll路由参数 const { params } = context; const infoId = params?.params[0]; const watchId = params?.params[1]; if (!infoId || !watchId) { return { notFound: true }; } const infoUrl = process.env.API_URL + `/info/${infoId}`; const watchUrl = process.env.API_URL + `/watch/${watchId}`; const infoUrlEnime = process.env.API_URL_GOGO + `/info/${infoId}`; const watchUrlEnime = process.env.API_URL_GOGO + `/watch/${watchId}`; try { let infoData, watchData, propsInfoId, propsWatchId; if (/^\d+$/.test(infoId)) { // 并行请求提升性能 [infoData, watchData] = await Promise.all([ axios.get(infoUrl), axios.get(watchUrl) ]); propsInfoId = infoId; propsWatchId = watchId; } else { [infoData, watchData] = await Promise.all([ axios.get(infoUrlEnime), axios.get(watchUrlEnime, { params: { server: "vidstreaming" } }) ]); propsInfoId = `gogo${infoId}`; propsWatchId = `gogo${watchId}`; } return { props: { infoData: infoData.data, watchData: watchData.data, infoId: propsInfoId, watchId: propsWatchId }, revalidate: 86400 // 可选:设置页面重新验证时间,按需更新缓存 }; } catch (err) { // 避免重定向循环,直接返回404或错误信息 return { notFound: true }; } };
关键注意点
getStaticPaths和getStaticProps是独立执行的,不存在参数传递的通道。- 使用
fallback: 'blocking'时,首次访问未预渲染的路径会在服务器端生成页面,之后访问会直接用缓存,兼顾性能和动态性。 - 错误处理避免重定向到当前页面,防止无限循环。
内容的提问来源于stack exchange,提问作者Viper
相关产品推荐
相关产品推荐

