-
Notifications
You must be signed in to change notification settings - Fork 40
feat: render async nested components #266
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
The changes in the MDCRenderer.vue component are actually not needed to support async rendering. I've updated the PR to only include detailed documentation and a playground example.
This PR closes nuxt-content#266. This PR resolves nuxt-content#263.
|
Unless I'm missing something, the functionality described in this PR actually works with no changes to the existing module, provided the "snippets" are implemented as described in the PR linked below (meaning, I think just more documentation is needed). @farnabaz I'm closing this PR in favor of #270 which provides the playground example and documentation. With "reusable content" being on the roadmap, I think adding the docs now in the linked PR is ideal. Adding the docs resolves my current need with no changes and opens the door for anyone else wanting to implement something similar. |
π Linked issue
#263
β Type of change
π Description
Note
The playground from this PR is available on StackBlitz
With the changes in this PR, the
MDCRendererwill also support rendering nested async components by wrapping any child components indefineAsyncComponentwhich will allow its tree to resolve their internal top-levelasync setup()before allowing the parentMDCRendererto resolve.This behavior allows for introducing MDC block components that themselves can perform async actions, such as fetching their own data before allowing the parent component to resolve.
In order for the parent
MDCRenderercomponent to properly wait for the child async component(s) to resolve:All functionality in the child component must be executed within an async setup function with top-level
await(if no async/await behavior is needed in the child, e.g. no data fetching, then the component will resolve normally).The child component's
templatecontent should be wrapped with the built-inSuspensecomponent with thesuspensibleprop set totrue.In a Nuxt application, this means that setting
immediate: falseon anyuseAsyncDataoruseFetchcalls would prevent the parentMDCRendererfrom waiting and the parent would potentially resolve before the child components have finished rendering, causing hydration errors or missing content.Example: MDC "snippets"
To demonstrate how powerful these nested async block components can be, you could allow users to define a subset of markdown documents in your project that will be utilized as reusable "snippets" in a parent document.
You would create a custom block component in your project that handles fetching the snippet markdown content from the API, use
parseMarkdownto get theastnodes, and render it in its ownMDCorMDCRenderercomponent.See the code in the playground
PageSnippetcomponent as an example, and to see the behavior in action, check out the playground by runningpnpm devand navigating to the/async-componentsroute.Handling recursion
If your project implements a "reusable snippet" type of approach, you will likely want to prevent the use of recursive snippets, whereby a nested
MDCRendererattempts to then load another child somewhere in its component tree with the same content (meaning, importing itself) and your application would be thrown into an infinite loop.One way to get around this is to utilize Vue's
provide/injectto pass down the history of rendered "snippets" so that a child can properly determine if it is being called recursively, and stop the chain. This can be used in combination with parsing theastdocument nodes after calling theparseMarkdownfunction to strip out recursive node trees from theastbefore rendering the content in the DOM.For an example on how to prevent infinite loops and recursion with this pattern, please see the code in the playground's
PageSnippetcomponent.