admin管理员组

文章数量:1418592

I've just started using NextJs getStaticProps, and static files generated at build time is neat. But my contents just don't stay unchanged, I need static files to be updated but rebuilding the app everytime there's a modification is costly. Is there a way to generate new static files only. getServerSideProps turned out to be taking a big amount of time til first byte.

I've just started using NextJs getStaticProps, and static files generated at build time is neat. But my contents just don't stay unchanged, I need static files to be updated but rebuilding the app everytime there's a modification is costly. Is there a way to generate new static files only. getServerSideProps turned out to be taking a big amount of time til first byte.

Share Improve this question edited Jun 11, 2021 at 21:52 Yves Ng asked Jun 11, 2021 at 13:16 Yves NgYves Ng 1372 silver badges12 bronze badges 2
  • 3 I'm not sure I get this right - you say your content changes, but then you say it's taking time to rebuild. I'm not sure there's any other option here, as the modified content needs to be rebuild :) – Andrey Popov Commented Jun 11, 2021 at 14:09
  • @AndreyPopov Yeah I meant rerun the static-file-generating function for new records. Sorry for the confusion :'D – Yves Ng Commented Jun 11, 2021 at 21:40
Add a ment  | 

2 Answers 2

Reset to default 4

Depending on type of you content Incremental Statatic Regeneration could be a good solution. But in my experience it introduced other problems when rendering catalog/category pages. As nextjs has no idea if one part of you data is dependent on some other it may render an old catalog page with a link to post or product which no longer exists. This usually happens in bination with a 'fallback' feature for dynamic routes.

It also won't refresh you page right after you made changes so you will see a result only after some time.

Possible workaround is to load posts/product on category page dynamically via ajax. You will lose a part of UX and SEO but on the other hand this solution is relatively easy to maintain.

There is also an option or rather hack to rebuild parts of saved content by directly accessing the cache in custom server. Add 'purge=1' to the page address you want to refreshed.

const { ServerResponse, createServer } = require('http')
const next = require('next')
const { promises } = require('fs')
const path = require('path')

const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

const purgeByPath = async url => {
  const pathname = url == '/' ? '/index' : url
  const fullPathname = `.next/server/pages${pathname}`
  const fullPathHTML = `${fullPathname}.html`
  const fullPathJSON = `${fullPathname}.json`

  try {
    await promises.unlink(fullPathHTML)
    await promises.unlink(fullPathJSON)
  } catch (err) {
    console.log(`Could not unlink cache files: ${err}`)
  }

  await app.incrementalCache.cache.del(pathname)

  let req = new Request(process.env.BASE_HOST + url)
  let res = new ServerResponse(req)

  await app.renderToHTML(req, res, url, { _nextDataReq: true })
}

app.prepare().then(() => {
  createServer(async (req, res) => {
    const url = new URL(req.url, "http://localhost:3000/")

    if (req.method == 'POST' && req.url == '/purge-cache') {
      let raw = ''
      req.on('data', chunk => raw += chunk)
      req.on('end', async () => {
        const data = JSON.parse(raw)

        if (!data || !data.urls) {
          res.statusCode = 400
          res.end()
          return
        }

        for (let url of data.urls) {
          console.log(`Cleaning cache on: ${url}`)
          await purgeByPath(url)
        }

        res.end()
      })
    } else if (url.searchParams.get('purge') == '1') {
      await purgeByPath(req.url)
      res.end()
    } else {
      handle(req, res)
    }
  }).listen(3000, (err) => {
    if (err) throw err
    console.log(`> Ready on http://localhost:3000/`)
  })
})

The static cache consists of two parts:

  • Static cache files located at .next/server/pages where each route will probably have two files html and json. Most likely you'll need to delete both.
  • In-memory cache. Once you app cached a page and saved it to memory it won't go to files on hard drive but instead will load content from memory. So you need to delete it as well.

Cache implementation is not documented an might be swapped or removed in future versions.

This won't work on vercel and has some issues with scaling. You should also add some kind of security token to purge routes.

If I understand it correctly, you are looking for Incremental Statatic Regeneration.

To enable it, you need to add a revalidate time in getStaticProps. As your content is changed and after the revalidation time is reached, a new static page will be generated and served by the server. Depends on how often your content changes, you can change the revalidation time accordingly.

export async function getStaticProps() {
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  return {
    props: {
      posts,
    },
    // Next.js will attempt to re-generate the page:
    // - When a request es in
    // - At most once every 10 seconds
    revalidate: 10, // In seconds
  }
}

Reference

https://nextjs/docs/basic-features/data-fetching#incremental-static-regeneration

本文标签: