-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
/
client-delayed-component.ts
91 lines (85 loc) · 3.15 KB
/
client-delayed-component.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import { createStaticVNode, createVNode, defineComponent, getCurrentInstance, h, onBeforeUnmount, onMounted, ref } from 'vue'
import type { Component, Ref, VNode } from 'vue'
// import ClientOnly from '#app/components/client-only'
import { useObserver } from '#app/utils'
import { getFragmentHTML } from '#app/components/utils'
import { useNuxtApp } from '#app/nuxt'
// todo find a better way to do it ?
function elementIsVisibleInViewport (el: Element) {
const { top, left, bottom, right } = el.getBoundingClientRect()
const { innerHeight, innerWidth } = window
return ((top > 0 && top < innerHeight) ||
(bottom > 0 && bottom < innerHeight)) &&
((left > 0 && left < innerWidth) || (right > 0 && right < innerWidth))
}
/* @__NO_SIDE_EFFECTS__ */
export const createLazyIOClientPage = (componentLoader: Component) => {
return defineComponent({
inheritAttrs: false,
setup (_, { attrs }) {
if (import.meta.server) {
return () => h('div', {}, [
h(componentLoader, attrs),
])
}
const nuxt = useNuxtApp()
const instance = getCurrentInstance()!
const isIntersecting = ref(false)
const el: Ref<Element | null> = ref(null)
let unobserve: (() => void) | null = null
// todo can be refactored
if (instance.vnode.el && nuxt.isHydrating) {
isIntersecting.value = elementIsVisibleInViewport(instance.vnode.el as Element)
}
if (!isIntersecting.value) {
onMounted(() => {
const observer = useObserver()
unobserve = observer!.observe(el.value as Element, () => {
isIntersecting.value = true
unobserve?.()
unobserve = null
})
})
}
onBeforeUnmount(() => {
unobserve?.()
unobserve = null
})
return () => {
return h('div', { ref: el }, [
isIntersecting.value ? h(componentLoader, attrs) : (instance.vnode.el && nuxt.isHydrating) ? createVNode(createStaticVNode(getFragmentHTML(instance.vnode.el ?? null, true)?.join('') || '', 1)) : null,
])
}
},
})
}
/* @__NO_SIDE_EFFECTS__ */
export const createLazyNetworkClientPage = (componentLoader: Component) => {
return defineComponent({
inheritAttrs: false,
setup (_, { attrs }) {
const nuxt = useNuxtApp()
const instance = getCurrentInstance()!
let vnode: VNode | null = null
if (import.meta.client && nuxt.isHydrating && instance.vnode?.el) {
vnode = createStaticVNode(getFragmentHTML(instance.vnode.el ?? null, true)?.join('') || '', 1)
}
const isIdle = ref(false)
let idleHandle: number | null = null
onMounted(() => {
idleHandle = requestIdleCallback(() => {
isIdle.value = true
cancelIdleCallback(idleHandle as unknown as number)
idleHandle = null
})
})
onBeforeUnmount(() => {
if (idleHandle) {
cancelIdleCallback(idleHandle as unknown as number)
idleHandle = null
}
})
return () => isIdle.value ? h(componentLoader, attrs) : vnode
},
})
}