-
-
Notifications
You must be signed in to change notification settings - Fork 8.4k
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
feat: lazy hydration strategies for async components #11458
Conversation
Size ReportBundles
Usages
|
@@ -118,6 +121,17 @@ export function defineAsyncComponent< | |||
|
|||
__asyncLoader: load, | |||
|
|||
__asyncHydrate(el, instance, hydrate) { | |||
const doHydrate = hydrateStrategy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to handle exceptions since user can pass a custom strategy? If an exception occurs in hydrateStrategy
, fall back to hydrate
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the behavior of falling back to hydrate should be optional, because if users want to use a custom hydration strategy, it often means that they have to implement a complete hydrate function themselves, so exception handling should also be one of the things that users need to consider.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. Although it should be noted that even if sync errors are handled, we can't catch all possible async errors in custom strategies, so there will still be a possibility for the hydration to never happen. In other words, error handling here can only mitigate errors to a certain extent - it's ultimately the user's responsibility to provide a working strategy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should strategies be provided with a way to do tear down logic? e.g. If an async component is removed before it is hydrated, the strategy may need to remove listeners on document
, or watchers on a store.
Good point, I should also add a test case for a non-hydrated tree to be removed before ever being hydrated. |
Can Vue methods like watch/ref be used inside custom hydration strategies? |
@GalacticHypernova |
Hey @edison1105 , // componentLoader is the dynamic import, attrs.hydrate is for the component to render
defineComponent({
inheritAttrs: false,
setup (_, { attrs }) {
if (import.meta.server) {
return () => h(defineAsyncComponent(componentLoader), attrs)
}
const shouldHydrate = ref(!!(attrs.hydrate ?? true))
if (shouldHydrate.value) {
return () => h(defineAsyncComponent(componentLoader), attrs)
}
const strategy: HydrationStrategy = (hydrate) => {
const unwatch = watch(shouldHydrate, () => hydrate(), { once: true })
return () => unwatch()
}
return () => h(defineAsyncComponent({loader: componentLoader, hydrate: strategy})
},
}) |
@GalacticHypernova |
Alright thank you! |
Async components can now control when they are hydrated by providing a hydration strategy.
Vue provides a number of built-in hydration strategies. These built-in strategies need to be individually imported so they can be tree-shaken if not used.
The design is intentionally low-level for flexibility. Compiler syntax sugar can potentially be built on top of this in the future either in core or in higher level solutions (e.g. Nuxt).
Why is it coupled to async components? Mostly because async components are already natural boundaries for async operations inside a render tree. Async sub trees require special handling in regards of reactivity tracking and suspense resolution. By coupling async hydration to async components we can support the feature without introducing extra complexities when it interacts with those other areas of concerns.
The result of this coupling is that one can't have a component that is eagerly loaded but lazily hydrated - which seems illogical to begin with.
Hydrate on Idle
Hydrates via
requestIdleCallback
:Hydrate on Visible
Hydrate when element(s) become visible via
IntersectionObserver
.Can optionally pass in an options object value for the observer:
Hydrate on Media Query
Hydrates when the specified media query matches.
Hydrate on Interaction
Hydrates when specified event(s) are triggered on the component element(s). The event that triggered the hydration will also be replayed once hydration is complete.
Can also be a list of multiple event types:
Custom Strategy