是否可以不重建整个应用程序而仅在 Nextjs 中生成新的静态文件?
Is it possible to not rebuild the whole app but generate new static files only in Nextjs?
我刚开始使用 NextJs getStaticProps
,在 build time
生成的静态文件整洁 .但我的内容并没有保持不变,我需要更新静态文件,但每次修改时重新构建应用程序的成本很高。有没有办法只生成新的静态文件。 getServerSideProps
原来在第一个字节之前花费了大量时间。
如果我没理解错的话,您正在寻找增量静态再生。
要启用它,您需要在getStaticProps
中添加一个revalidate
时间。当您的内容发生变化并且达到重新生效时间后,将生成一个新的静态页面并由服务器提供。根据您的内容更改频率,您可以相应地更改重新生效时间。
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 comes in
// - At most once every 10 seconds
revalidate: 10, // In seconds
}
}
参考
https://nextjs.org/docs/basic-features/data-fetching#incremental-static-regeneration
根据您的内容类型,Incremental Statatic Regeneration 可能是一个很好的解决方案。但根据我的经验,它在呈现 catalog/category 页面时引入了其他问题。由于 nextjs 不知道您的数据的一部分是否依赖于其他数据,它可能会呈现一个旧的目录页面,其中包含 link 到 post 或不再存在的产品。这通常与动态路由的 'fallback' 功能结合使用。
它也不会在您进行更改后立即刷新您的页面,因此您只能在一段时间后才能看到结果。
可能的解决方法是通过 ajax 在类别页面上动态加载 posts/product。你会失去一部分用户体验和搜索引擎优化,但另一方面这个解决方案相对容易维护。
还有一个选项或者更确切地说是黑客通过直接访问自定义服务器中的缓存来重建部分保存的内容。要刷新的页面地址加上'purge=1'
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/`)
})
})
静态缓存由两部分组成:
- 位于
.next/server/pages
的静态缓存文件,其中每个路由可能有两个文件 html 和 json。您很可能需要同时删除两者。
- 内存缓存。一旦您的应用程序缓存了一个页面并将其保存到内存中,它就不会转到硬盘驱动器上的文件,而是从内存中加载内容。所以你也需要删除它。
未记录缓存实现,可能会在未来版本中交换或删除。
这在 vercel 上不起作用,并且在缩放方面存在一些问题。您还应该添加某种安全令牌来清除路由。
我刚开始使用 NextJs getStaticProps
,在 build time
生成的静态文件整洁 .但我的内容并没有保持不变,我需要更新静态文件,但每次修改时重新构建应用程序的成本很高。有没有办法只生成新的静态文件。 getServerSideProps
原来在第一个字节之前花费了大量时间。
如果我没理解错的话,您正在寻找增量静态再生。
要启用它,您需要在getStaticProps
中添加一个revalidate
时间。当您的内容发生变化并且达到重新生效时间后,将生成一个新的静态页面并由服务器提供。根据您的内容更改频率,您可以相应地更改重新生效时间。
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 comes in
// - At most once every 10 seconds
revalidate: 10, // In seconds
}
}
参考
https://nextjs.org/docs/basic-features/data-fetching#incremental-static-regeneration
根据您的内容类型,Incremental Statatic Regeneration 可能是一个很好的解决方案。但根据我的经验,它在呈现 catalog/category 页面时引入了其他问题。由于 nextjs 不知道您的数据的一部分是否依赖于其他数据,它可能会呈现一个旧的目录页面,其中包含 link 到 post 或不再存在的产品。这通常与动态路由的 'fallback' 功能结合使用。
它也不会在您进行更改后立即刷新您的页面,因此您只能在一段时间后才能看到结果。
可能的解决方法是通过 ajax 在类别页面上动态加载 posts/product。你会失去一部分用户体验和搜索引擎优化,但另一方面这个解决方案相对容易维护。
还有一个选项或者更确切地说是黑客通过直接访问自定义服务器中的缓存来重建部分保存的内容。要刷新的页面地址加上'purge=1'
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/`)
})
})
静态缓存由两部分组成:
- 位于
.next/server/pages
的静态缓存文件,其中每个路由可能有两个文件 html 和 json。您很可能需要同时删除两者。 - 内存缓存。一旦您的应用程序缓存了一个页面并将其保存到内存中,它就不会转到硬盘驱动器上的文件,而是从内存中加载内容。所以你也需要删除它。
未记录缓存实现,可能会在未来版本中交换或删除。
这在 vercel 上不起作用,并且在缩放方面存在一些问题。您还应该添加某种安全令牌来清除路由。