Skip to content

Commit 2e61e9c

Browse files
committed
perf: update virtual-list
1 parent a3a0600 commit 2e61e9c

File tree

4 files changed

+70
-26
lines changed

4 files changed

+70
-26
lines changed

components/vc-select/OptionList.tsx

+8-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import classNames from '../_util/classNames';
55
import pickAttrs from '../_util/pickAttrs';
66
import { isValidElement } from '../_util/props-util';
77
import createRef from '../_util/createRef';
8-
import { computed, defineComponent, reactive, VNodeChild, watch } from 'vue';
8+
import { computed, defineComponent, nextTick, reactive, VNodeChild, watch } from 'vue';
99
import List from '../vc-virtual-list/List';
1010
import {
1111
OptionsType as SelectOptionsType,
@@ -153,8 +153,14 @@ const OptionList = defineComponent<OptionListProps, { state?: any }>({
153153
scrollIntoView(index);
154154
}
155155
});
156+
// Force trigger scrollbar visible when open
157+
if (props.open) {
158+
nextTick(()=>{
159+
listRef.current?.scrollTo(undefined);
160+
})
161+
}
156162
},
157-
{ immediate: true, flush: 'post' },
163+
{ immediate: true },
158164
);
159165

160166
// ========================== Values ==========================

components/vc-virtual-list/List.tsx

+34-19
Original file line numberDiff line numberDiff line change
@@ -84,16 +84,13 @@ const List = defineComponent({
8484
},
8585
setup(props) {
8686
// ================================= MISC =================================
87-
87+
const useVirtual = computed(()=>{
88+
const { height, itemHeight, virtual } = props;
89+
return !!(virtual !== false && height && itemHeight);
90+
})
8891
const inVirtual = computed(() => {
89-
const { height, itemHeight, data, virtual } = props;
90-
return !!(
91-
virtual !== false &&
92-
height &&
93-
itemHeight &&
94-
data &&
95-
itemHeight * data.length > height
96-
);
92+
const { height, itemHeight, data } = props;
93+
return useVirtual.value && data && itemHeight * data.length > height;
9794
});
9895

9996
const state = reactive<ListState>({
@@ -103,7 +100,8 @@ const List = defineComponent({
103100
});
104101

105102
const componentRef = ref<HTMLDivElement>();
106-
103+
const fillerInnerRef = ref<HTMLDivElement>();
104+
const scrollBarRef = ref<any>(); // Hack on scrollbar to enable flash call
107105
// =============================== Item Key ===============================
108106
const getKey = (item: Record<string, any>) => {
109107
if (typeof props.itemKey === 'function') {
@@ -135,17 +133,27 @@ const List = defineComponent({
135133

136134
// ================================ Height ================================
137135
const [setInstance, collectHeight, heights, heightUpdatedMark] = useHeights(getKey, null, null);
138-
139136
// ========================== Visible Calculation =========================
140137
const calRes = computed(() => {
141-
if (!inVirtual.value) {
138+
if (!useVirtual.value) {
142139
return {
143140
scrollHeight: undefined,
144141
start: 0,
145142
end: state.mergedData.length - 1,
146143
offset: undefined,
147144
};
148145
}
146+
147+
// Always use virtual scroll bar in avoid shaking
148+
if (!inVirtual.value) {
149+
return {
150+
scrollHeight: fillerInnerRef.value?.offsetHeight || 0,
151+
start: 0,
152+
end: state.mergedData.length - 1,
153+
offset: undefined,
154+
};
155+
}
156+
149157
let itemTop = 0;
150158
let startIndex: number | undefined;
151159
let startOffset: number | undefined;
@@ -228,7 +236,7 @@ const List = defineComponent({
228236

229237
// Since this added in global,should use ref to keep update
230238
const [onRawWheel, onFireFoxScroll] = useFrameWheel(
231-
inVirtual,
239+
useVirtual,
232240
isScrollAtTop,
233241
isScrollAtBottom,
234242
offsetY => {
@@ -240,7 +248,7 @@ const List = defineComponent({
240248
);
241249

242250
// Mobile touch move
243-
useMobileTouchMove(inVirtual, componentRef, (deltaY, smoothOffset) => {
251+
useMobileTouchMove(useVirtual, componentRef, (deltaY, smoothOffset) => {
244252
if (originScroll(deltaY, smoothOffset)) {
245253
return false;
246254
}
@@ -250,7 +258,7 @@ const List = defineComponent({
250258
});
251259
// Firefox only
252260
function onMozMousePixelScroll(e: MouseEvent) {
253-
if (inVirtual.value) {
261+
if (useVirtual.value) {
254262
e.preventDefault();
255263
}
256264
}
@@ -285,14 +293,17 @@ const List = defineComponent({
285293
getKey,
286294
collectHeight,
287295
syncScrollTop,
296+
() => {
297+
scrollBarRef.value?.delayHidden();
298+
},
288299
);
289300

290301
const componentStyle = computed(() => {
291302
let cs: CSSProperties | null = null;
292303
if (props.height) {
293304
cs = { [props.fullHeight ? 'height' : 'maxHeight']: props.height + 'px', ...ScrollStyle };
294305

295-
if (inVirtual.value) {
306+
if (useVirtual.value) {
296307
cs!.overflowY = 'hidden';
297308

298309
if (state.scrollMoving) {
@@ -310,11 +321,13 @@ const List = defineComponent({
310321
onFallbackScroll,
311322
onScrollBar,
312323
componentRef,
313-
inVirtual,
324+
useVirtual,
314325
calRes,
315326
collectHeight,
316327
setInstance,
317328
sharedConfig,
329+
scrollBarRef,
330+
fillerInnerRef
318331
};
319332
},
320333
render() {
@@ -341,7 +354,7 @@ const List = defineComponent({
341354
componentStyle,
342355
onFallbackScroll,
343356
onScrollBar,
344-
inVirtual,
357+
useVirtual,
345358
collectHeight,
346359
sharedConfig,
347360
setInstance,
@@ -375,13 +388,15 @@ const List = defineComponent({
375388
height={scrollHeight}
376389
offset={offset}
377390
onInnerResize={collectHeight}
391+
ref="fillerInnerRef"
378392
>
379393
{listChildren}
380394
</Filler>
381395
</Component>
382396

383-
{inVirtual && (
397+
{useVirtual && (
384398
<ScrollBar
399+
ref="scrollBarRef"
385400
prefixCls={prefixCls}
386401
scrollTop={scrollTop}
387402
height={height}

components/vc-virtual-list/ScrollBar.tsx

+19-4
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ export default defineComponent({
137137
const enableScrollRange = this.getEnableScrollRange();
138138
const enableHeightRange = this.getEnableHeightRange();
139139

140-
const ptg = newTop / enableHeightRange;
140+
const ptg = enableHeightRange ? newTop / enableHeightRange : 0;
141141
const newScrollTop = Math.ceil(ptg * enableScrollRange);
142142
this.moveRaf = raf(() => {
143143
onScroll(newScrollTop);
@@ -164,30 +164,45 @@ export default defineComponent({
164164

165165
getEnableScrollRange() {
166166
const { scrollHeight, height } = this.$props;
167-
return scrollHeight - height;
167+
return scrollHeight - height || 0;
168168
},
169169

170170
getEnableHeightRange() {
171171
const { height } = this.$props;
172172
const spinHeight = this.getSpinHeight();
173-
return height - spinHeight;
173+
return height - spinHeight || 0;
174174
},
175175

176176
getTop() {
177177
const { scrollTop } = this.$props;
178178
const enableScrollRange = this.getEnableScrollRange();
179179
const enableHeightRange = this.getEnableHeightRange();
180+
if (scrollTop === 0 || enableScrollRange === 0) {
181+
return 0;
182+
}
180183
const ptg = scrollTop / enableScrollRange;
181184
return ptg * enableHeightRange;
182185
},
186+
// Not show scrollbar when height is large thane scrollHeight
187+
getVisible () {
188+
const { visible } = this.state;
189+
const { height, scrollHeight } = this.$props;
190+
191+
if (height >= scrollHeight) {
192+
return false;
193+
}
194+
195+
return visible;
196+
},
183197
},
184198

185199
render() {
186200
// eslint-disable-next-line no-unused-vars
187-
const { visible, dragging } = this.state;
201+
const { dragging } = this.state;
188202
const { prefixCls } = this.$props;
189203
const spinHeight = this.getSpinHeight() + 'px';
190204
const top = this.getTop() + 'px';
205+
const visible = this.getVisible();
191206
return (
192207
<div
193208
ref={this.scrollbarRef}

components/vc-virtual-list/hooks/useScrollTo.tsx

+9-1
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,18 @@ export default function useScrollTo(
1212
getKey: GetKey,
1313
collectHeight: () => void,
1414
syncScrollTop: (newTop: number) => void,
15+
triggerFlash: () => void,
1516
) {
1617
let scroll: number | null = null;
1718

18-
return (arg: any) => {
19+
return (arg?: any) => {
20+
// When not argument provided, we think dev may want to show the scrollbar
21+
if (arg === null || arg === undefined) {
22+
triggerFlash();
23+
return;
24+
}
25+
26+
// Normal scroll logic
1927
raf.cancel(scroll!);
2028
const data = state.mergedData;
2129
const itemHeight = props.itemHeight;

0 commit comments

Comments
 (0)