vitepress
vitepress copied to clipboard
Dynamic route content loading as a function
Is your feature request related to a problem? Please describe.
I'm currently working on a solution to create a vitepress site from a CMS, which contains quite a few pages. We are using dynamic routes to fetch the existing data and are essentially using one template and one path loader, as we cannot determine previously how the site is structured and generate the appropriate path loader for each part of the site. However, because we need to load all the content into memory to return the list in the paths loader, we are reaching very high memory usage which we would like to mitigate to avoid future problems as the content in the CMS will grow.
Describe the solution you'd like
The solution I'm proposing is to allow content in the return of paths() to be provided as a function (possibly an async one) , so that the content can be loaded as each page is generated and not previously, thus avoiding the memory overload.
Describe alternatives you've considered
No response
Additional context
No response
Validations
- [X] Follow our Code of Conduct
- [X] Read the docs.
- [X] Read the Contributing Guidelines.
- [X] Check that there isn't already an issue that asks for the same feature to avoid creating a duplicate.
Can you try with rc.33, the memory usage during build should be bit improved with this update.
Regarding paths(), I think getters for params/content should already work:
// [id].path.ts
export default {
async paths() {
const pages = await getListOfPages()
return Promise.all(
pages.map(async (page) => {
return {
get params() {
return getParams(page)
},
get content() {
return getContent(page)
}
}
})
)
}
}
ah, wait, it doesn't support promises returned by getters. But if we support that will this syntax be good enough for you?
Can you try with rc.33, the memory usage during build should be bit improved with this update.
Regarding
paths(), I think getters for params/content should already work:// [id].path.ts export default { async paths() { const pages = await getListOfPages() return Promise.all( pages.map(async (page) => { return { get params() { return getParams(page) }, get content() { return getContent(page) } } }) ) } }ah, wait, it doesn't support promises returned by getters. But if we support that will this syntax be good enough for you?
How about an async generator?
Async generators (async function* paths) won't work, because we need the list of pages (params) beforehand. And if you're talking about async getters (something like async get content()), that doesn't exist in JS.
Async generators (
async function* paths) won't work, because we need the list of pages (params) beforehand. And if you're talking about async getters (something likeasync get content()), that doesn't exist in JS.
I meant async function *(). I am currently doing experiments to maximize parallelism during build. And AFAIK async generators should always be preferred because in the worst case you can treat it as Promise<Array>, and the user can either provide (1) an array, (2) an iterator/generator, (3) promise of an array or iterator, or (4) an async generator.
I've implemented something similar here, and it speeded up search indexing significantly.
BTW, async getters do not exist, but we can return a Promise from the getter. That's not an issue.
but we can return a
Promisefrom the getter
Yeah that's what I wrote earlier. That can be done.
And AFAIK async generators should always be preferred because in the worst case you can treat it as
Promise<Array>
Yeah but we need that array beforehand because we need to generate list of dynamic routes (dynamicRoutesPlugin.ts). content is only thing that's retrieved on demand 👀
@brc-dd Yeah you're right. In this case just make content Awaitable should be good enough.
I'd like to hear your comments on implementing build-time parallelization though. I need some thoughts on its feasibility and scope so I can decide if I should keep working on that (and how likely it would be accepted 👀).
build-time parallelization
Probably we can track it at #3183. In vite-ssg, Kael had done this - https://github.com/vuetifyjs/vite-ssg/commit/1f1663a666b3c5fdc30a543b5cf42737fc4b4722
Our rendering is parallel enough I guess, but bundling process can get slow if using large libraries. That stuff needs to be improved at vite/rollup level. The vite team is continuously working on perf stuff and next year we probably will see rolldown being integrated there.
If there are things that should be done in vitepress to further improve perf, I'm totally open for PRs/discussions.
Can you try with rc.33, the memory usage during build should be bit improved with this update.
Regarding
paths(), I think getters for params/content should already work:// [id].path.ts export default { async paths() { const pages = await getListOfPages() return Promise.all( pages.map(async (page) => { return { get params() { return getParams(page) }, get content() { return getContent(page) } } }) ) } }ah, wait, it doesn't support promises returned by getters. But if we support that will this syntax be good enough for you?
Hello, yes that syntax would be perfectly fine if implemented, thanks!
Also, I experimented with rc.33 in terms of memory and did not experience much of a decrease, I'm currently working with about 450 pages of varying lengths and occupied around 1 GB of memory in my few experiments (both with rc.33 and the previous version I was using - rc.31 I believe).
1 GB memory is kind of expected. If you want to reduce it further, you can set buildConcurrency to some lower value in vitepress config. But I doubt if it will change things much. If you are on lower memory systems like some CI or VPS, then you probably should do something like NODE_OPTIONS='--max-old-space-size=600' vitepress build -- this should make node perform garbage collection more frequently. But both of these will reduce the speed of build process.
Also, even if we support on-demand loading of content, the memory difference won't be that much. It'll be around 10-20 MB I guess (basically some constant factor of your content size; though it will still improve performance because of the network factor with loading all those promises at once).