Skip to content

Commit 3a63bde

Browse files
feat(client): lazy render large data (#181)
1 parent 5e09851 commit 3a63bde

File tree

5 files changed

+73
-14
lines changed

5 files changed

+73
-14
lines changed

packages/client/src/components/inspector/InspectorStateField.vue

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
<script setup lang="ts">
22
import type { InspectorCustomState, InspectorState, InspectorStateEditorPayload } from '@vue/devtools-kit'
3-
import { sortByKey } from '@vue/devtools-shared'
3+
import { isArray, isObject, sortByKey } from '@vue/devtools-shared'
44
import { formatInspectorStateValue, getInspectorStateValueType, getRawValue } from '@vue/devtools-kit'
55
import { useDevToolsBridgeRpc } from '@vue/devtools-core'
6+
import { VueButton, VueIcon, VTooltip as vTooltip } from '@vue/devtools-ui'
67
import Actions from './InspectorDataField/Actions.vue'
78
import type { EditorAddNewPropType } from '~/composables/inspector'
89
@@ -15,6 +16,8 @@ const props = withDefaults(defineProps<{
1516
depth: 0,
1617
})
1718
19+
const STATE_FIELDS_LIMIT_SIZE = 30
20+
1821
const state = useStateEditorContext()
1922
const bridgeRpc = useDevToolsBridgeRpc()
2023
const value = computed(() => formatInspectorStateValue(props.data.value))
@@ -49,11 +52,13 @@ const normalizedValue = computed(() => {
4952
5053
const rawValue = computed(() => getRawValue(props.data.value))
5154
55+
const limit = ref(STATE_FIELDS_LIMIT_SIZE)
56+
5257
const normalizedChildField = computed(() => {
53-
let { value, inherit } = rawValue.value
54-
if (Array.isArray(value)) {
55-
// @TODO: show more
56-
const sliced = value.slice(0, 20)
58+
const { value, inherit } = rawValue.value
59+
let displayedValue: any[]
60+
if (isArray(value)) {
61+
const sliced = value.slice(0, limit.value)
5762
return sliced.map((item, i) => ({
5863
key: `${props.data.key}.${i}`,
5964
value: item,
@@ -62,22 +67,32 @@ const normalizedChildField = computed(() => {
6267
creating: false,
6368
}))
6469
}
65-
else if (value !== null && typeof value === 'object') {
66-
value = Object.keys(value).map(key => ({
70+
else if (isObject(value)) {
71+
displayedValue = Object.keys(value).slice(0, limit.value).map(key => ({
6772
key: `${props.data.key}.${key}`,
6873
value: value[key],
6974
...inherit,
7075
editable: props.data.editable,
7176
creating: false,
7277
}))
7378
if (type.value !== 'custom')
74-
value = sortByKey(value)
79+
displayedValue = sortByKey(displayedValue)
7580
}
7681
else {
77-
value = []
82+
displayedValue = []
7883
}
7984
80-
return value === props.data.value ? {} : value
85+
return displayedValue === props.data.value ? {} : displayedValue
86+
})
87+
88+
const fieldsCount = computed(() => {
89+
const { value } = rawValue.value
90+
if (isArray(value))
91+
return value.length
92+
else if (isObject(value))
93+
return Object.keys(value).length
94+
else
95+
return 0
8196
})
8297
8398
const normalizedDisplayedKey = computed(() => {
@@ -150,8 +165,8 @@ function submitDrafting() {
150165
resetDrafting()
151166
}
152167
153-
const containerRef = ref()
154-
const { isHovering } = useHover(containerRef)
168+
const containerRef = ref<HTMLDivElement>()
169+
const { isHovering } = useHover(() => containerRef.value)
155170
</script>
156171

157172
<template>
@@ -194,6 +209,11 @@ const { isHovering } = useHover(containerRef)
194209
<InspectorStateField
195210
v-for="(field, index) in normalizedChildField" :key="index" :data="field" :depth="depth + 1" :no="no" :root-id="rootId"
196211
/>
212+
<VueButton v-if="fieldsCount > limit" v-tooltip="'Show more'" flat size="mini" class="ml-4" @click="limit += STATE_FIELDS_LIMIT_SIZE">
213+
<template #icon>
214+
<VueIcon icon="i-material-symbols-more-horiz" />
215+
</template>
216+
</VueButton>
197217
<div v-if="draftingNewProp.enable" :style="{ paddingLeft: `${(depth + 1) * 15 + 4}px` }">
198218
<span overflow-hidden text-ellipsis whitespace-nowrap state-key>
199219
<EditInput v-model="draftingNewProp.key" type="string" :show-actions="false" />

packages/client/src/composables/hover.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { useEventListener } from '@vueuse/core'
2-
import type { MaybeRef } from '@vueuse/core'
2+
import type { MaybeRefOrGetter } from '@vueuse/core'
33

44
export interface UseHoverOptions {
55
enter?: () => void
66
leave?: () => void
77
initital?: boolean
88
}
99

10-
export function useHover(el: MaybeRef<EventTarget | null | undefined>, options: UseHoverOptions = {}) {
10+
export function useHover(el: MaybeRefOrGetter<HTMLElement | null | undefined>, options: UseHoverOptions = {}) {
1111
const {
1212
enter = () => { },
1313
leave = () => { },
@@ -23,6 +23,7 @@ export function useHover(el: MaybeRef<EventTarget | null | undefined>, options:
2323
isHovering.value = false
2424
leave()
2525
})
26+
2627
return {
2728
isHovering,
2829
}

packages/playground/src/App.preview.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { ref } from 'vue'
33
import { useI18n } from 'vue-i18n'
44
import STC from './components/ScrollToComponent.vue'
5+
import LargeData from './components/LargeData.vue'
56
import { useAppStore } from './stores'
67
78
import { availableLocales, loadLanguageAsync } from './modules/i18n'
@@ -51,6 +52,7 @@ async function toggleLocales() {
5152
Hello
5253
</router-link>
5354
</p>
55+
<LargeData />
5456
<STC />
5557
</div>
5658
</template>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<script setup lang="ts">
2+
import { ref } from 'vue'
3+
4+
const arr = ref(Array.from({ length: 10000 }).fill(0).map((_, i) => i))
5+
const object = ref(Object.fromEntries(arr.value.map(i => [i, i])))
6+
const map = ref(new Map(arr.value.map(i => [i, i])))
7+
const set = ref(new Set(arr.value))
8+
</script>
9+
10+
<template>
11+
<h1>Large Data Test</h1>
12+
<div>
13+
<p>Array Length: {{ arr.length }}</p>
14+
<p>Object Length: {{ Object.keys(object).length }}</p>
15+
<p>Map Size: {{ map.size }}</p>
16+
<p>Set Size: {{ set.size }}</p>
17+
</div>
18+
</template>

packages/shared/src/general.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,21 @@ export const deepClone = rfdc({ circles: true })
5858
export function randomStr() {
5959
return Math.random().toString(36).slice(2)
6060
}
61+
62+
// #region assert types
63+
export function isObject<T extends object>(value: any): value is T {
64+
return typeof value === 'object' && !Array.isArray(value) && value !== null
65+
}
66+
67+
export function isArray<T>(value: any): value is T[] {
68+
return Array.isArray(value)
69+
}
70+
71+
export function isSet<T>(value: any): value is Set<T> {
72+
return value instanceof Set
73+
}
74+
75+
export function isMap<K, V>(value: any): value is Map<K, V> {
76+
return value instanceof Map
77+
}
78+
// #endregion

0 commit comments

Comments
 (0)