在生产环境中构建Next.js静态网站时出现提取错误

17

当我使用npm run build导出生产环境时,我不理解这些错误,但是当我测试npm run dev时,它可以正常工作。我使用getStaticPropsgetStaticPath从API路由获取数据。

首先,当我执行npm run build时:

FetchError: invalid json response body at https://main-website-next.vercel.app/api/products reason: Unexpected token T in JSON at position
0
    at D:\zummon\Main Website\main-website-next\node_modules\node-fetch\lib\index.js:272:32
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at async getStaticPaths (D:\zummon\Main Website\main-website-next\.next\server\pages\product\[slug].js:1324:18)
    at async buildStaticPaths (D:\zummon\Main Website\main-website-next\node_modules\next\dist\build\utils.js:16:80)
    at async D:\zummon\Main Website\main-website-next\node_modules\next\dist\build\utils.js:26:612
    at async D:\zummon\Main Website\main-website-next\node_modules\next\dist\build\tracer.js:1:1441 {
  type: 'invalid-json'
}

\pages\product\[slug]

import { assetPrefix } from '../../next.config'

export default function Page(){...}

export const getStaticProps = async ({ params: { slug }, locale }) => {
  const res = await fetch(`${assetPrefix}/api/products/${slug}`)
  const result = await res.json()
  const data = result.filter(item => item.locale === locale)[0]
  const { title, keywords, description } = data
  return {
    props: {
      data,
      description,
      keywords, 
      title
    }
  }
}

export const getStaticPaths = async () => {
  const res = await fetch(`${assetPrefix}/api/products`)
  const result = await res.json()
  const paths = result.map(({ slug, locale }) => ({ params: { slug: slug }, locale }))
  return {
    fallback: true,
    paths,
  }
}

next.config.js

const isProd = process.env.NODE_ENV === 'production'

module.exports = {
  assetPrefix: isProd ? 'https://main-website-next.vercel.app' : 'http://localhost:3000',
  i18n: {
    localeDetection: false,
    locales: ['en', 'th'],
    defaultLocale: 'en',
  }
}

API路由

// pages/api/products/index.js
import data from '../../../data/products'
export default (req, res) => {
  res.status(200).json(data)
}

// pages/api/products/[slug].js
import db from '../../../data/products'
export default ({ query: { slug } }, res) => {
  const data = db.filter(item => item.slug === slug)
  if (data.length > 0) {
    res.status(200).json(data)
  } else {
    res.status(404).json({ message: `${slug} not found` })
  }
}

// ../../../data/products (data source)
module.exports = [
  { locale: "en", slug: "google-sheets-combine-your-cashflow",
    title: "Combine your cashflow",
    keywords: ["Google Sheets","accounting"],
    description: "...",
  },
    ...
]

当我删除生产域名后,运行npm run build仍然出现错误,如下所示。
TypeError: Only absolute URLs are supported
    at getNodeRequestOptions (D:\zummon\Main Website\main-website-next\node_modules\node-fetch\lib\index.js:1305:9)
    at D:\zummon\Main Website\main-website-next\node_modules\node-fetch\lib\index.js:1410:19
    at new Promise (<anonymous>)
    at fetch (D:\zummon\Main Website\main-website-next\node_modules\node-fetch\lib\index.js:1407:9)
    at getStaticPaths (D:\zummon\Main Website\main-website-next\.next\server\pages\[slug].js:938:21)
    at buildStaticPaths (D:\zummon\Main Website\main-website-next\node_modules\next\dist\build\utils.js:16:86)
    at D:\zummon\Main Website\main-website-next\node_modules\next\dist\build\utils.js:26:618
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at async D:\zummon\Main Website\main-website-next\node_modules\next\dist\build\tracer.js:1:1441 {
  type: 'TypeError'
}

删除后我的next.config.js

const isProd = process.env.NODE_ENV === 'production'

module.exports = {      //remove
  assetPrefix: isProd ? '' : 'http://localhost:3000',
  i18n: {
    localeDetection: false,
    locales: ['en', 'th'],
    defaultLocale: 'en',
  }
}

当我运行命令npm run build时,我的package.json文件会被使用。

{
  "name": "main-website-next",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build && next export",
    "start": "next start"
  },
  "dependencies": {
    "next": "10.0.6",
    "react": "17.0.1",
    "react-dom": "17.0.1"
  }
}

你能分享一下你的API路由代码吗?与其调用API路由,你应该直接在getStaticProps/getStaticPaths中使用数据获取逻辑。 - juliomalves
1
谢谢。我已经在问题上面添加了更多信息,如果我理解正确的话,如果不是你要找的,请告诉我你需要什么。我对Next.js非常新手。 - zummon
@juliomalves 直接使用数据获取逻辑 fetch('${assetPrefix}/api/products/${slug}') 更改为 fetch('${assetPrefix}/data/products') 或者 fetch(/next/server...) 对吗?但我不知道正确的方式。另外,我只在其项目内获取数据。 - zummon
2个回答

25
getStaticProps中不应该调用内部API路由。相反,您可以直接在getStaticProps/getStaticPaths中使用API逻辑。这些仅在服务器端发生,因此您可以直接编写服务器端代码
由于getStaticProps仅在服务器端运行,因此它永远不会在客户端上运行。 它甚至不会包含在浏览器的JS包中,因此您可以直接编写数据库查询而无需将其发送到浏览器。
这意味着,您可以在getStaticProps中直接编写服务器端代码,而不是从getStaticProps获取API route(它本身从外部源获取数据)。
此外,在构建时期,您的API路由不可用,因为此时尚未启动服务器。
这是您的代码的小重构,以解决问题。
// /pages/product/[slug]

import db from '../../../data/products'

// Remaining code..

export const getStaticProps = async ({ params: { slug }, locale }) => {
    const result = db.filter(item => item.slug === slug)
    const data = result.filter(item => item.locale === locale)[0]
    const { title, keywords, description } = data
    return {
        props: {
            data,
            description,
            keywords, 
            title
        }
    }
}

export const getStaticPaths = async () => {
    const paths = db.map(({ slug, locale }) => ({ params: { slug: slug }, locale }))
    return {
        fallback: true,
        paths,
    }
}

1
谢谢,我现在理解更多了。但是这次显示“Error occurred prerendering page "/en/product/[slug]"-TypeError: Cannot read property 'contents' of undefined”,但是测试服务器运行正常。 - zummon
请确保对数据进行检查,以避免访问未定义的属性,从而触发类似于该错误的错误。 - juliomalves
1
非常感谢,这应该已经修复了我所提出的问题。对于我面临的下一个错误超出了范围,还有一些我可以自己解决但我还没有看到的东西。*我以为只是测试npm run dev就可以正常工作,但显然在生产构建时情况并非如此。 - zummon
@juliomalves 我该如何执行未定义检查? - Sushilzzz
1
刚刚完成了NextJS的启动应用程序,关于“为什么不应在getStatic*内使用API路由”的解释还不够清晰,但是你的“在构建时API路由不可用,因为此时服务器尚未启动”非常好,并且完全合理。 - TimPietrusky
显示剩余10条评论

0
  1. 我正在获取位于/public文件夹中的JSON文件;
  2. 为了将这些JSON文件传递给生产环境,我首先删除了在getStaticPathsgetStaticProps中执行获取操作的文件(例如/pages/[category]/[id].tsx);
  3. 然后构建通过了,但应用程序部分功能无法正常工作;
  4. 现在将这些文件(例如/pages/[category]/[id].tsx)放回去,再次构建,它就可以正常工作了;
  5. 我知道这种方法很奇怪,但也许对其他人有帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接