import { _next, parentNode } from './dom/node'
import { invokeArrayFns } from '@vue/shared'
-export type AsyncHydrateImplFn = (
- el: Element,
- instance: VaporComponentInstance,
- hydrate: () => void,
- load: () => Promise<VaporComponent>,
- getResolvedComp: () => VaporComponent | undefined,
- hydrateStrategy: any,
-) => void
-
-let asyncHydrate: AsyncHydrateImplFn | undefined
-
-// Register async hydrate implementation for tree-shaking
-export function registerAsyncHydrateImpl(): void {
- asyncHydrate = asyncHydrateImpl
-}
-
-const asyncHydrateImpl: AsyncHydrateImplFn = (
- el: Element,
- instance: VaporComponentInstance,
- hydrate: () => void,
- load: () => Promise<VaporComponent>,
- getResolvedComp: () => VaporComponent | undefined,
- hydrateStrategy: any,
-) => {
- // Create placeholder block that matches the adopted DOM.
- // The async component may get unmounted before its inner component is loaded,
- // so we need to give it a placeholder block.
- if (isComment(el, '[')) {
- const end = _next(locateEndAnchor(el)!)
- const block = (instance.block = [el as Node])
- let cur = el as Node
- while (true) {
- let n = _next(cur)
- if (n && n !== end) {
- block.push((cur = n))
- } else {
- break
- }
- }
- } else {
- instance.block = el
- }
-
- // Mark as mounted to ensure it can be unmounted before
- // its inner component is resolved
- instance.isMounted = true
-
- // Advance current hydration node to the nextSibling
- setCurrentHydrationNode(
- isComment(el, '[') ? locateEndAnchor(el)! : el.nextSibling,
- )
-
- // If async component needs to be updated before hydration, hydration is no longer needed.
- let isHydrated = false
- watch(
- () => instance.attrs,
- () => {
- // early return if already hydrated
- if (isHydrated) return
-
- // call the beforeUpdate hook to avoid calling hydrate in performAsyncHydrate
- instance.bu && invokeArrayFns(instance.bu)
-
- // mount the inner component and remove the placeholder
- const parent = parentNode(el)!
- load().then(() => {
- if (instance.isUnmounted) return
- hydrate()
- if (isComment(el, '[')) {
- const endAnchor = locateEndAnchor(el)!
- removeFragmentNodes(el, endAnchor)
- insert(instance.block, parent, endAnchor)
- } else {
- insert(instance.block, parent, el)
- remove(el, parent)
- }
- })
- },
- { deep: true, once: true },
- )
-
- performAsyncHydrate(
- el,
- instance,
- () => {
- hydrateNode(el, () => {
- hydrate()
- insert(instance.block, parentNode(el)!, el)
- isHydrated = true
- })
- },
- getResolvedComp,
- load,
- hydrateStrategy,
- )
-}
-
/*@ __NO_SIDE_EFFECTS__ */
export function defineVaporAsyncComponent<T extends VaporComponent>(
source: AsyncComponentLoader<T> | AsyncComponentOptions<T>,
// not the actual hydrate function
hydrate: () => void,
) {
- asyncHydrate &&
- asyncHydrate(
- el,
- instance,
- hydrate,
- load,
- getResolvedComp,
- hydrateStrategy,
- )
+ // early return allows tree-shaking of hydration logic when not used
+ if (!isHydrating) return
+
+ // Create placeholder block that matches the adopted DOM.
+ // The async component may get unmounted before its inner component is loaded,
+ // so we need to give it a placeholder block.
+ if (isComment(el, '[')) {
+ const end = _next(locateEndAnchor(el)!)
+ const block = (instance.block = [el as Node])
+ let cur = el as Node
+ while (true) {
+ let n = _next(cur)
+ if (n && n !== end) {
+ block.push((cur = n))
+ } else {
+ break
+ }
+ }
+ } else {
+ instance.block = el
+ }
+
+ // Mark as mounted to ensure it can be unmounted before
+ // its inner component is resolved
+ instance.isMounted = true
+
+ // Advance current hydration node to the nextSibling
+ setCurrentHydrationNode(
+ isComment(el, '[') ? locateEndAnchor(el)! : el.nextSibling,
+ )
+
+ // If async component needs to be updated before hydration, hydration is no longer needed.
+ let isHydrated = false
+ watch(
+ () => instance.attrs,
+ () => {
+ // early return if already hydrated
+ if (isHydrated) return
+
+ // call the beforeUpdate hook to avoid calling hydrate in performAsyncHydrate
+ instance.bu && invokeArrayFns(instance.bu)
+
+ // mount the inner component and remove the placeholder
+ const parent = parentNode(el)!
+ load().then(() => {
+ if (instance.isUnmounted) return
+ hydrate()
+ if (isComment(el, '[')) {
+ const endAnchor = locateEndAnchor(el)!
+ removeFragmentNodes(el, endAnchor)
+ insert(instance.block, parent, endAnchor)
+ } else {
+ insert(instance.block, parent, el)
+ remove(el, parent)
+ }
+ })
+ },
+ { deep: true, once: true },
+ )
+
+ performAsyncHydrate(
+ el,
+ instance,
+ () => {
+ hydrateNode(el, () => {
+ hydrate()
+ insert(instance.block, parentNode(el)!, el)
+ isHydrated = true
+ })
+ },
+ getResolvedComp,
+ load,
+ hydrateStrategy,
+ )
},
get __asyncResolved() {