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

如何在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",
   ...
  },
  ...
 ]
}

问题分析

  1. 核心逻辑错误:getStaticPaths无法接收getStaticProps返回的props。这两个函数是独立执行的:getStaticPaths在构建阶段先运行,用来定义需要预渲染的路径;之后才会为每个路径执行getStaticProps。你试图从getStaticProps传参到getStaticPaths的思路完全不成立。
  2. 无限循环原因:getStaticPaths中没有合法的参数输入,导致返回的paths为空,加上fallback: false,Next.js会返回404。如果你的serversDown错误处理函数存在重定向到当前页面的逻辑,就会触发无限循环。
  3. 其他代码问题:
    • 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 20:12:06