Javascript 下一个JS构建是';不要把每一条路都建起来

Javascript 下一个JS构建是';不要把每一条路都建起来,javascript,reactjs,next.js,Javascript,Reactjs,Next.js,摘要/问题 我使用Nextjs创建了一个动画数据库应用程序,部署在Vercel上。构建很好,呈现了初始页面,但只有少数动态路由被呈现,其余显示404页面。我查看了部署日志,发现对于每个动态页面,每个动态路由只构建了10条路由 从Vercel部署屏幕截图 在开发过程中(localhost:3000),没有任何问题,一切正常 路线基于每个标题的id,有数千个标题 我的代码 下面是我使用getstaticpath和getStaticProps export const getStaticProps

摘要/问题

我使用
Nextjs
创建了一个动画数据库应用程序,部署在
Vercel
上。构建很好,呈现了初始页面,但只有少数动态路由被呈现,其余显示404页面。我查看了部署日志,发现对于每个动态页面,每个动态路由只构建了10条路由

从Vercel部署屏幕截图

在开发过程中(localhost:3000),没有任何问题,一切正常

路线基于每个标题的
id
,有数千个标题

我的代码

下面是我使用
getstaticpath
getStaticProps

export const getStaticProps = async ({ params }) => {
  const [anime, animeCharacters, categories, streaming, reviews] = await Promise.all([
    fetch(`https://kitsu.io/api/edge/anime/${params.id}`),
    fetch(`https://kitsu.io/api/edge/anime/${params.id}/characters`),
    fetch(`https://kitsu.io/api/edge/anime/${params.id}/categories`),
    fetch(`https://kitsu.io/api/edge/anime/${params.id}/streaming-links`),
    fetch(`https://kitsu.io/api/edge/anime/${params.id}/reviews`),
  ])
    .then((responses) =>
      Promise.all(responses.map((response) => response.json()))
    )
    .catch((e) => console.log(e, "There was an error retrieving the data"))

  return { props: { anime, animeCharacters, categories, streaming, reviews } }
}

export const getStaticPaths = async () => {
  const res = await fetch("https://kitsu.io/api/edge/anime")
  const anime = await res.json()

  const paths = anime.data.map((show) => ({
    params: { id: show.id },
  }))

  return { paths, fallback: false }
}
[id]
是我的动态路线,正如您所见,它只填充了10条路线(前3条和7条额外路线)

尽管有这么多的节目,我还是在每个节目上循环,获取它的ID,然后将其作为路径传递

我的想法

我使用的API就是API

在文档中,它表示:“默认情况下,资源以10为一组进行分页,最多可以增加到20”。我想这可能是生成10条路径的原因,但如果是这样,为什么它在生产和部署中都能正常工作?此外,当我点击每个海报图像时,它应该通过其
id
将我带到特定的标题,这是动态的,因此最初生成多少资源并不重要

动态页面代码`/anime/[id]

import { useState } from "react"
import { useRouter } from 'next/router'
import fetch from "isomorphic-unfetch"
import formatedDates from "./../../helpers/formatDates"

import Navbar from "../../components/Navbar"
import TrailerVideo from "../../components/TrailerVideo"
import Characters from "./../../components/Characters"
import Categories from "../../components/Categories"
import Streamers from "../../components/Streamers"
import Reviews from "../../components/Reviews"

const Post = ({ anime, animeCharacters, categories, streaming, reviews}) => {
  const [readMore, setReadMore] = useState(false)

  const handleReadMore = () => setReadMore((prevState) => !prevState)

  let {
    titles: { en, ja_jp },
    synopsis,
    startDate,
    endDate,
    ageRating,
    ageRatingGuide,
    averageRating,
    episodeCount,
    posterImage: { small },
    coverImage,
    youtubeVideoId,
  } = anime.data.attributes

  const defaultImg = "/cover-img-default.jpg"

  const synopsisSubString = () =>
    !readMore ? synopsis.substring(0, 240) : synopsis.substring(0, 2000)

  const router = useRouter()
  if(router.isFallback) return <div>loading...</div>

  return (
    <div className='relative'>
      <div className='z-0'>
        <img
          className='absolute mb-4 h-12 min-h-230 w-full object-cover opacity-50'
          src={!coverImage ? defaultImg : coverImage.large}
        />
      </div>
      <div className='relative container z-50'>
        <Navbar />

        <div className='mt-16 flex flex-wrap md:flex-no-wrap'>
          {/* Main  */}
          <div className='md:max-w-284'>
            <img className='z-50 mb-6' src={small} />

            <div className='xl:text-lg pb-6'>
              <h1 className='mb-2'>Anime Details</h1>
              <ul>
                <li>
                  <span className='font-bold'>Japanese Title:</span> {ja_jp}
                </li>
                <li>
                  <span className='font-bold'>Aired:</span>{" "}
                  {formatedDates(startDate, endDate)}
                </li>
                <li>
                  <span className='font-bold'>Rating:</span> {ageRating} /{" "}
                  {ageRatingGuide}
                </li>
                <li>
                  <span className='font-bold'>Episodes:</span> {episodeCount}
                </li>
              </ul>
            </div>

            <Streamers streaming={streaming} />
          </div>

          {/* Info Section */}
          <div className='flex flex-wrap lg:flex-no-wrap md:flex-1 '>
            <div className='mt-6 md:mt-40 md:ml-6 lg:mr-10'>
              <h1 className='sm:text-3xl pb-1'>{en}</h1>
              <h2 className='sm:text-xl lg:text-2xl pb-4 text-yellow-600'>
                {averageRating}{" "}
                <span className='text-white text-base lg:text-lg'>
                  Community Rating
                </span>
              </h2>
              <div>
                <p className='max-w-2xl pb-3 overflow-hidden xl:text-lg'>
                  {synopsisSubString()}
                  <span className={!readMore ? "inline" : "hidden"}>...</span>
                </p>
                <button
                  className='text-teal-500 hover:text-teal-900 transition ease-in-out duration-500 focus:outline-none focus:shadow-outline'
                  onClick={handleReadMore}
                >
                  {!readMore ? "Read More" : "Read Less"}
                </button>
              </div>
              <Categories categories={categories} />
              <Reviews reviews={reviews}/>
            </div>

            {/* Sidebar */}
            <section className='lg:max-w-sm mt-10 md:ml-6 lg:ml-0'>
              <TrailerVideo youtubeVideoId={youtubeVideoId} />
              <Characters animeCharacters={animeCharacters} />
            </section>
          </div>
        </div>
      </div>
    </div>
  )
}

export const getStaticProps = async ({ params }) => {
  const [anime, animeCharacters, categories, streaming, reviews] = await Promise.all([
    fetch(`https://kitsu.io/api/edge/anime/${params.id}`),
    fetch(`https://kitsu.io/api/edge/anime/${params.id}/characters`),
    fetch(`https://kitsu.io/api/edge/anime/${params.id}/categories`),
    fetch(`https://kitsu.io/api/edge/anime/${params.id}/streaming-links`),
    fetch(`https://kitsu.io/api/edge/anime/${params.id}/reviews`),
  ])
    .then((responses) =>
      Promise.all(responses.map((response) => response.json()))
    )
    .catch((e) => console.log(e, "There was an error retrieving the data"))

  return { props: { anime, animeCharacters, categories, streaming, reviews } }
}

export const getStaticPaths = async () => {
  const res = await fetch("https://kitsu.io/api/edge/anime")
  const anime = await res.json()

  const paths = anime.data.map((show) => ({
    params: { id: show.id },
  }))

  return { paths, fallback: true }
}

export default Post
从“react”导入{useState}
从“下一个/路由器”导入{useRouter}
从“同构取消蚀刻”导入提取
从“/./../helpers/formatDates”导入格式化日期
从“../../components/Navbar”导入导航栏
从“../../components/TrailerVideo”导入TrailerVideo
从“/./../components/Characters”导入字符
从“../../components/Categories”导入类别
从“../../components/Streamers”导入拖缆
从“../../components/Reviews”导入评论
const Post=({动画、动画角色、类别、流媒体、评论})=>{
const[readMore,setReadMore]=useState(false)
const handleReadMore=()=>setReadMore((prevState)=>!prevState)
让{
标题:{en,ja_jp},
提要
开始日期,
结束日期,
阿格拉廷,
阿格拉廷圭德,
平均而言,
情节数,
后期图像:{small},
封面图片,
YouTubevidoid,
}=anime.data.attributes
const defaultImg=“/cover img default.jpg”
const synopsisSubString=()=>
!readMore?大纲.子字符串(0240):大纲.子字符串(02000)
const router=useRouter()
如果(router.isFallback)返回加载。。。
返回(
{/*Main*/}
动漫细节
  • 日文标题:{ja_jp}
  • 播放:{“} {格式化日期(开始日期,结束日期)}
  • 评级:{ageRating}/{”“} {Agratingguide}
  • 情节:{eposodecount}
{/*信息部分*/} {en} {平均值}{”“} 社区评级

{synopsisSubString()} ...

{!阅读更多?“阅读更多”:“阅读更少”} {/*侧边栏*/} ) } export const getStaticProps=async({params})=>{ const[动画、动画角色、类别、流媒体、评论]=等待承诺。全部([ 取回(`https://kitsu.io/api/edge/anime/${params.id}`), 取回(`https://kitsu.io/api/edge/anime/${params.id}/characters`), 取回(`https://kitsu.io/api/edge/anime/${params.id}/categories`), 取回(`https://kitsu.io/api/edge/anime/${params.id}/streaming links`), 取回(`https://kitsu.io/api/edge/anime/${params.id}/reviews`), ]) 。然后((回答)=> Promise.all(responses.map((response)=>response.json()) ) .catch((e)=>console.log(e,“检索数据时出错”)) 返回{道具:{动画、动画角色、类别、流媒体、评论} } export const getstaticpath=async()=>{ const res=等待获取(“https://kitsu.io/api/edge/anime") const anime=wait res.json() 常量路径=anime.data.map((显示)=>({ 参数:{id:show.id}, })) 返回{路径,回退:true} } 导出默认帖子
错误截图


如果您正在使用的API以10人一组的形式提供资源,那么当您在
getstaticpath
中调用API时,您只提前了10个
id
。在nextjs中使用静态生成在生产模式下提前为所有可用id构建静态页面。但在开发模式下,服务器将根据每个请求重新创建每个页面。因此,要解决这个问题,您可以构建前10个页面,并使其余页面成为回退页面。这是你怎么做的

export const getStaticPaths = async () => {
  const res = await fetch("https://kitsu.io/api/edge/anime")
  const anime = await res.json()

  // you can make a series of calls to the API requesting 
  // the next page to get the desired amount of data (100 or 1000)
  // how many ever static pages you want to build ahead of time

  const paths = anime.data.map((show) => ({
    params: { id: show.id },
  }))

  // this will generate 10(resource limit if you make 1 call because your API returns only 10 resources) 
  // pages ahead of time  and rest of the pages will be fallback
  return { paths, fallback: true }
}

请记住,在
getstaticpath
中使用
{fallback:true}
时,您需要有某种加载指示器,因为当您第一次发出请求时,页面将静态生成,其中
function MyPage = (props) {

  const router = useRouter()

  if (router.isFallback) {
    // your loading indicator
    return <div>loading...</div>
  }

  return (
    // the normal logic for your page
  )
}

const getStaticProps = async () => {
  // your data fetching logic

  // if fail
  return {
    props: {data: null, error: true, statusCode: 'the-status-code-you-want-to-send-(500 or 404)'} 
  }  

  // if success 
  return {
    props: {data: 'my-fetched-data', error: false} 
  }

}

// in the page component
import ErrorPage from 'next/error';

function MyStaticPage(props) {
  if (props.error) {
   return <ErrorPage statusCode={404}/>
  }

  // else you normal page logic
}