javascript - 下一个 JS 构建并不是构建每条路径

标签 javascript reactjs next.js

摘要/问题

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

部署 Vercel 的屏幕截图

enter image description here

在开发过程中(localhost:3000),没有出现任何问题,一切都运行良好。

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

我的代码

这是我使用 getStaticPathsgetStaticProps 的其中一个页面的代码

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 是 Kitsu 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

错误截图

enter image description here

Repo

最佳答案

如果您使用的 API 以 10 个为一组提供资源,那么当您在 getStaticPaths 中调用 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 }
}

请记住,在 getStaticPaths 中使用 {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
  )
}

附注我忘记提及如何处理 API 响应 404 或 500 且在静态生成中使用后备时不存在可作为 props 发送的资源的错误。

以下是具体操作方法。

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
}

请告诉我它是否有帮助或者您在实现时遇到了一些错误。 您可以在这里了解更多https://nextjs.org/docs/basic-features/data-fetching#getstaticpaths-static-generation

关于javascript - 下一个 JS 构建并不是构建每条路径,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61941296/

相关文章:

javascript - 为什么 React.js 在使用标记库时不加载 HTML 代码

javascript - Next.js getServerSideProps 在本地、PR 预览和生产中使用/api

next.js - Tailwind css 边框颜色在网页上不起作用

javascript - 为什么变量 'name' 在第一次使用后不需要初始化 [Javascript]

JavaScript:错误 - "Expected an assignment or function call and instead saw an expression"?

javascript - 在聊天框中自动滚动 javascript 函数

reactjs - 在 .NET Core 3 中替换 UseWebpackDevMiddleware?

javascript - 如何在单击按钮时更改 onclick 函数值

javascript - Redux - 尝试向configureStore添加功能

javascript - 无法正确使用 Next.js 进行获取